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
);
140 Log
.e(TAG
, "Package " + basePackageName
+ " cannot be installed from "
141 + apkName
+ ": " + msg
+ " (returnCode " + returnCode
+ ")");
142 counter
.appInstallFinished();
145 }.getBinder(), 0, SYSTEM_SERVER_PACKAGE_NAME
, userId
);
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
;
154 AppInstallCounter(Context context
, int userId
) {
156 this.userId
= userId
;
159 synchronized void appInstallFinished() {
160 this.finishedCount
++;
161 checkIfAllFinished();
164 synchronized void setExpectedAppsCount(int expectedCount
) {
165 this.expectedCount
= expectedCount
;
166 checkIfAllFinished();
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
);