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 }