Home | History | Annotate | Download | only in retaildemo
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.server.retaildemo;
     18 
     19 import android.app.AppGlobals;
     20 import android.app.PackageInstallObserver;
     21 import android.content.Context;
     22 import android.content.pm.IPackageManager;
     23 import android.content.pm.PackageManager;
     24 import android.os.Bundle;
     25 import android.os.Environment;
     26 import android.os.RemoteException;
     27 import android.os.UserHandle;
     28 import android.provider.Settings;
     29 import android.util.ArrayMap;
     30 import android.util.Log;
     31 import android.util.Slog;
     32 
     33 import com.android.internal.annotations.VisibleForTesting;
     34 import com.android.internal.util.ArrayUtils;
     35 
     36 import java.io.File;
     37 import java.io.IOException;
     38 import java.util.Collections;
     39 import java.util.Map;
     40 
     41 /**
     42  * Helper class for installing preloaded APKs
     43  */
     44 class PreloadAppsInstaller {
     45     private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
     46     private static String TAG = PreloadAppsInstaller.class.getSimpleName();
     47     private static final String PRELOAD_APK_EXT = ".apk.preload";
     48     private static boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     49 
     50     private final IPackageManager mPackageManager;
     51     private final File preloadsAppsDirectory;
     52     private final Context mContext;
     53 
     54     private final Map<String, String> mApkToPackageMap;
     55 
     56     PreloadAppsInstaller(Context context) {
     57         this(context, AppGlobals.getPackageManager(), Environment.getDataPreloadsAppsDirectory());
     58     }
     59 
     60     @VisibleForTesting
     61     PreloadAppsInstaller(Context context, IPackageManager packageManager, File preloadsAppsDirectory) {
     62         mContext = context;
     63         mPackageManager = packageManager;
     64         mApkToPackageMap = Collections.synchronizedMap(new ArrayMap<>());
     65         this.preloadsAppsDirectory = preloadsAppsDirectory;
     66     }
     67 
     68     void installApps(int userId) {
     69         File[] files = preloadsAppsDirectory.listFiles();
     70         AppInstallCounter counter = new AppInstallCounter(mContext, userId);
     71         if (ArrayUtils.isEmpty(files)) {
     72             counter.setExpectedAppsCount(0);
     73             return;
     74         }
     75         int expectedCount = 0;
     76         for (File file : files) {
     77             String apkName = file.getName();
     78             if (apkName.endsWith(PRELOAD_APK_EXT) && file.isFile()) {
     79                 String packageName = mApkToPackageMap.get(apkName);
     80                 if (packageName != null) {
     81                     try {
     82                         expectedCount++;
     83                         installExistingPackage(packageName, userId, counter);
     84                     } catch (Exception e) {
     85                         Slog.e(TAG, "Failed to install existing package " + packageName, e);
     86                     }
     87                 } else {
     88                     try {
     89                         installPackage(file, userId, counter);
     90                         expectedCount++;
     91                     } catch (Exception e) {
     92                         Slog.e(TAG, "Failed to install package from " + file, e);
     93                     }
     94                 }
     95             }
     96         }
     97         counter.setExpectedAppsCount(expectedCount);
     98     }
     99 
    100     private void installExistingPackage(String packageName, int userId,
    101             AppInstallCounter counter) {
    102         if (DEBUG) {
    103             Log.d(TAG, "installExistingPackage " + packageName + " u" + userId);
    104         }
    105         try {
    106             mPackageManager.installExistingPackageAsUser(packageName, userId);
    107         } catch (RemoteException e) {
    108             throw e.rethrowFromSystemServer();
    109         } finally {
    110             counter.appInstallFinished();
    111         }
    112     }
    113 
    114     private void installPackage(File file, final int userId, AppInstallCounter counter)
    115             throws IOException, RemoteException {
    116         final String apkName = file.getName();
    117         if (DEBUG) {
    118             Log.d(TAG, "installPackage " + apkName + " u" + userId);
    119         }
    120         mPackageManager.installPackageAsUser(file.getPath(), new PackageInstallObserver() {
    121             @Override
    122             public void onPackageInstalled(String basePackageName, int returnCode, String msg,
    123                     Bundle extras) {
    124                 if (DEBUG) {
    125                     Log.d(TAG, "Package " + basePackageName + " installed u" + userId
    126                             + " returnCode: " + returnCode + " msg: " + msg);
    127                 }
    128                 // Don't notify the counter for now, we'll do it in installExistingPackage
    129                 if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
    130                     mApkToPackageMap.put(apkName, basePackageName);
    131                     // Install on user 0 so that the package is cached when demo user is re-created
    132                     installExistingPackage(basePackageName, UserHandle.USER_SYSTEM, counter);
    133                 } else if (returnCode == PackageManager.INSTALL_FAILED_ALREADY_EXISTS) {
    134                     // This can only happen in first session after a reboot
    135                     if (!mApkToPackageMap.containsKey(apkName)) {
    136                         mApkToPackageMap.put(apkName, basePackageName);
    137                     }
    138                     installExistingPackage(basePackageName, userId, counter);
    139                 } else {
    140                     Log.e(TAG, "Package " + basePackageName + " cannot be installed from "
    141                             + apkName + ": " + msg + " (returnCode " + returnCode + ")");
    142                     counter.appInstallFinished();
    143                 }
    144             }
    145         }.getBinder(), 0, SYSTEM_SERVER_PACKAGE_NAME, userId);
    146     }
    147 
    148     private static class AppInstallCounter {
    149         private int expectedCount = -1; // -1 means expectedCount not set
    150         private int finishedCount;
    151         private final Context mContext;
    152         private final int userId;
    153 
    154         AppInstallCounter(Context context, int userId) {
    155             mContext = context;
    156             this.userId = userId;
    157         }
    158 
    159         synchronized void appInstallFinished() {
    160             this.finishedCount++;
    161             checkIfAllFinished();
    162         }
    163 
    164         synchronized void setExpectedAppsCount(int expectedCount) {
    165             this.expectedCount = expectedCount;
    166             checkIfAllFinished();
    167         }
    168 
    169         private void checkIfAllFinished() {
    170             if (expectedCount == finishedCount) {
    171                 Log.i(TAG, "All preloads finished installing for user " + userId);
    172                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
    173                         Settings.Secure.DEMO_USER_SETUP_COMPLETE, "1", userId);
    174             }
    175         }
    176     }
    177 }