OSDN Git Service

Correctly handle a case when there are no preloaded apps
[android-x86/frameworks-base.git] / services / retaildemo / java / com / android / server / retaildemo / PreloadAppsInstaller.java
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                 }
140             }
141         }.getBinder(), 0, SYSTEM_SERVER_PACKAGE_NAME, userId);
142     }
143
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;
149
150         AppInstallCounter(Context context, int userId) {
151             mContext = context;
152             this.userId = userId;
153         }
154
155         synchronized void appInstallFinished() {
156             this.finishedCount++;
157             checkIfAllFinished();
158         }
159
160         synchronized void setExpectedAppsCount(int expectedCount) {
161             this.expectedCount = expectedCount;
162             checkIfAllFinished();
163         }
164
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);
170             }
171         }
172     }
173 }