2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.server.retaildemo;
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;
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.internal.util.ArrayUtils;
37 import java.io.IOException;
38 import java.util.Collections;
42 * Helper class for installing preloaded APKs
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);
50 private final IPackageManager mPackageManager;
51 private final File preloadsAppsDirectory;
52 private final Context mContext;
54 private final Map<String, String> mApkToPackageMap;
56 PreloadAppsInstaller(Context context) {
57 this(context, AppGlobals.getPackageManager(), Environment.getDataPreloadsAppsDirectory());
61 PreloadAppsInstaller(Context context, IPackageManager packageManager, File preloadsAppsDirectory) {
63 mPackageManager = packageManager;
64 mApkToPackageMap = Collections.synchronizedMap(new ArrayMap<>());
65 this.preloadsAppsDirectory = preloadsAppsDirectory;
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);
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) {
83 installExistingPackage(packageName, userId, counter);
84 } catch (Exception e) {
85 Slog.e(TAG, "Failed to install existing package " + packageName, e);
89 installPackage(file, userId, counter);
91 } catch (Exception e) {
92 Slog.e(TAG, "Failed to install package from " + file, e);
97 counter.setExpectedAppsCount(expectedCount);
100 private void installExistingPackage(String packageName, int userId,
101 AppInstallCounter counter) {
103 Log.d(TAG, "installExistingPackage " + packageName + " u" + userId);
106 mPackageManager.installExistingPackageAsUser(packageName, userId);
107 } catch (RemoteException e) {
108 throw e.rethrowFromSystemServer();
110 counter.appInstallFinished();
114 private void installPackage(File file, final int userId, AppInstallCounter counter)
115 throws IOException, RemoteException {
116 final String apkName = file.getName();
118 Log.d(TAG, "installPackage " + apkName + " u" + userId);
120 mPackageManager.installPackageAsUser(file.getPath(), new PackageInstallObserver() {
122 public void onPackageInstalled(String basePackageName, int returnCode, String msg,
125 Log.d(TAG, "Package " + basePackageName + " installed u" + userId
126 + " returnCode: " + returnCode + " msg: " + msg);
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);
138 installExistingPackage(basePackageName, userId, counter);
141 }.getBinder(), 0, SYSTEM_SERVER_PACKAGE_NAME, userId);
144 private static class AppInstallCounter {
145 private int expectedCount = -1; // -1 means expectedCount not set
146 private int finishedCount;
147 private final Context mContext;
148 private final int userId;
150 AppInstallCounter(Context context, int userId) {
152 this.userId = userId;
155 synchronized void appInstallFinished() {
156 this.finishedCount++;
157 checkIfAllFinished();
160 synchronized void setExpectedAppsCount(int expectedCount) {
161 this.expectedCount = expectedCount;
162 checkIfAllFinished();
165 private void checkIfAllFinished() {
166 if (expectedCount == finishedCount) {
167 Log.i(TAG, "All preloads finished installing for user " + userId);
168 Settings.Secure.putStringForUser(mContext.getContentResolver(),
169 Settings.Secure.DEMO_USER_SETUP_COMPLETE, "1", userId);