blob: 90c58d0279a7b3e0edaa45f70c37968d309e4d20 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.retaildemo;
import android.app.AppGlobals;
import android.app.PackageInstallObserver;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
/**
* Helper class for installing preloaded APKs
*/
class PreloadAppsInstaller {
private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
private static String TAG = PreloadAppsInstaller.class.getSimpleName();
private static final String PRELOAD_APK_EXT = ".apk.preload";
private static boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final IPackageManager mPackageManager;
private final File preloadsAppsDirectory;
private final Context mContext;
private final Map<String, String> mApkToPackageMap;
PreloadAppsInstaller(Context context) {
this(context, AppGlobals.getPackageManager(), Environment.getDataPreloadsAppsDirectory());
}
@VisibleForTesting
PreloadAppsInstaller(Context context, IPackageManager packageManager, File preloadsAppsDirectory) {
mContext = context;
mPackageManager = packageManager;
mApkToPackageMap = Collections.synchronizedMap(new ArrayMap<>());
this.preloadsAppsDirectory = preloadsAppsDirectory;
}
void installApps(int userId) {
File[] files = preloadsAppsDirectory.listFiles();
AppInstallCounter counter = new AppInstallCounter(mContext, userId);
if (ArrayUtils.isEmpty(files)) {
counter.setExpectedAppsCount(0);
return;
}
int expectedCount = 0;
for (File file : files) {
String apkName = file.getName();
if (apkName.endsWith(PRELOAD_APK_EXT) && file.isFile()) {
String packageName = mApkToPackageMap.get(apkName);
if (packageName != null) {
try {
expectedCount++;
installExistingPackage(packageName, userId, counter);
} catch (Exception e) {
Slog.e(TAG, "Failed to install existing package " + packageName, e);
}
} else {
try {
installPackage(file, userId, counter);
expectedCount++;
} catch (Exception e) {
Slog.e(TAG, "Failed to install package from " + file, e);
}
}
}
}
counter.setExpectedAppsCount(expectedCount);
}
private void installExistingPackage(String packageName, int userId,
AppInstallCounter counter) {
if (DEBUG) {
Log.d(TAG, "installExistingPackage " + packageName + " u" + userId);
}
try {
mPackageManager.installExistingPackageAsUser(packageName, userId,
0 /*installFlags*/, PackageManager.INSTALL_REASON_UNKNOWN);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} finally {
counter.appInstallFinished();
}
}
private void installPackage(File file, final int userId, AppInstallCounter counter)
throws IOException, RemoteException {
final String apkName = file.getName();
if (DEBUG) {
Log.d(TAG, "installPackage " + apkName + " u" + userId);
}
mPackageManager.installPackageAsUser(file.getPath(), new PackageInstallObserver() {
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
if (DEBUG) {
Log.d(TAG, "Package " + basePackageName + " installed u" + userId
+ " returnCode: " + returnCode + " msg: " + msg);
}
// Don't notify the counter for now, we'll do it in installExistingPackage
if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
mApkToPackageMap.put(apkName, basePackageName);
// Install on user 0 so that the package is cached when demo user is re-created
installExistingPackage(basePackageName, UserHandle.USER_SYSTEM, counter);
} else if (returnCode == PackageManager.INSTALL_FAILED_ALREADY_EXISTS) {
// This can only happen in first session after a reboot
if (!mApkToPackageMap.containsKey(apkName)) {
mApkToPackageMap.put(apkName, basePackageName);
}
installExistingPackage(basePackageName, userId, counter);
} else {
Log.e(TAG, "Package " + basePackageName + " cannot be installed from "
+ apkName + ": " + msg + " (returnCode " + returnCode + ")");
counter.appInstallFinished();
}
}
}.getBinder(), 0, SYSTEM_SERVER_PACKAGE_NAME, userId);
}
private static class AppInstallCounter {
private int expectedCount = -1; // -1 means expectedCount not set
private int finishedCount;
private final Context mContext;
private final int userId;
AppInstallCounter(Context context, int userId) {
mContext = context;
this.userId = userId;
}
synchronized void appInstallFinished() {
this.finishedCount++;
checkIfAllFinished();
}
synchronized void setExpectedAppsCount(int expectedCount) {
this.expectedCount = expectedCount;
checkIfAllFinished();
}
private void checkIfAllFinished() {
if (expectedCount == finishedCount) {
Log.i(TAG, "All preloads finished installing for user " + userId);
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.DEMO_USER_SETUP_COMPLETE, "1", userId);
}
}
}
}