PackageInstallerService.java revision 3a44f3f1b446315ef894e01d2ab9b5388c2bd8c4
1/* 2 * Copyright (C) 2014 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 17package com.android.server.pm; 18 19import static android.content.pm.PackageManager.INSTALL_ALL_USERS; 20import static android.content.pm.PackageManager.INSTALL_FROM_ADB; 21import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING; 22 23import android.app.AppOpsManager; 24import android.content.Context; 25import android.content.pm.IPackageDeleteObserver; 26import android.content.pm.IPackageInstaller; 27import android.content.pm.IPackageInstallerSession; 28import android.content.pm.PackageInstallerParams; 29import android.os.Binder; 30import android.os.FileUtils; 31import android.os.HandlerThread; 32import android.os.Process; 33import android.os.UserHandle; 34import android.os.UserManager; 35import android.util.ArraySet; 36import android.util.Slog; 37import android.util.SparseArray; 38 39import com.android.internal.annotations.GuardedBy; 40import com.android.internal.util.ArrayUtils; 41import com.android.server.IoThread; 42import com.google.android.collect.Sets; 43 44import java.io.File; 45 46public class PackageInstallerService extends IPackageInstaller.Stub { 47 private static final String TAG = "PackageInstaller"; 48 49 // TODO: destroy sessions with old timestamps 50 // TODO: remove outstanding sessions when installer package goes away 51 52 private final Context mContext; 53 private final PackageManagerService mPm; 54 private final AppOpsManager mAppOps; 55 56 private final File mStagingDir; 57 58 private final HandlerThread mInstallThread = new HandlerThread(TAG); 59 private final Callback mCallback = new Callback(); 60 61 @GuardedBy("mSessions") 62 private int mNextSessionId; 63 @GuardedBy("mSessions") 64 private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>(); 65 66 public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) { 67 mContext = context; 68 mPm = pm; 69 mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 70 71 mStagingDir = stagingDir; 72 mStagingDir.mkdirs(); 73 74 synchronized (mSessions) { 75 readSessionsLocked(); 76 77 // Clean up orphaned staging directories 78 final ArraySet<String> dirs = Sets.newArraySet(mStagingDir.list()); 79 for (int i = 0; i < mSessions.size(); i++) { 80 dirs.remove(Integer.toString(mSessions.keyAt(i))); 81 } 82 for (String dirName : dirs) { 83 Slog.w(TAG, "Deleting orphan session " + dirName); 84 final File dir = new File(mStagingDir, dirName); 85 FileUtils.deleteContents(dir); 86 dir.delete(); 87 } 88 } 89 } 90 91 private void readSessionsLocked() { 92 // TODO: implement persisting 93 mSessions.clear(); 94 mNextSessionId = 1; 95 } 96 97 private void writeSessionsLocked() { 98 // TODO: implement persisting 99 } 100 101 private void writeSessionsAsync() { 102 IoThread.getHandler().post(new Runnable() { 103 @Override 104 public void run() { 105 synchronized (mSessions) { 106 writeSessionsLocked(); 107 } 108 } 109 }); 110 } 111 112 @Override 113 public int createSession(int userId, String installerPackageName, 114 PackageInstallerParams params) { 115 final int callingUid = Binder.getCallingUid(); 116 mPm.enforceCrossUserPermission(callingUid, userId, false, TAG); 117 mAppOps.checkPackage(callingUid, installerPackageName); 118 119 if (mPm.isUserRestricted(UserHandle.getUserId(callingUid), 120 UserManager.DISALLOW_INSTALL_APPS)) { 121 throw new SecurityException("User restriction prevents installing"); 122 } 123 124 if ((callingUid == Process.SHELL_UID) || (callingUid == 0)) { 125 params.installFlags |= INSTALL_FROM_ADB; 126 } else { 127 params.installFlags &= ~INSTALL_FROM_ADB; 128 params.installFlags &= ~INSTALL_ALL_USERS; 129 params.installFlags |= INSTALL_REPLACE_EXISTING; 130 } 131 132 synchronized (mSessions) { 133 final int sessionId = allocateSessionIdLocked(); 134 final long createdMillis = System.currentTimeMillis(); 135 final File sessionDir = new File(mStagingDir, Integer.toString(sessionId)); 136 sessionDir.mkdirs(); 137 138 final PackageInstallerSession session = new PackageInstallerSession(mCallback, mPm, 139 sessionId, userId, installerPackageName, callingUid, params, createdMillis, 140 sessionDir, mInstallThread.getLooper()); 141 mSessions.put(sessionId, session); 142 143 writeSessionsAsync(); 144 return sessionId; 145 } 146 } 147 148 @Override 149 public IPackageInstallerSession openSession(int sessionId) { 150 synchronized (mSessions) { 151 final PackageInstallerSession session = mSessions.get(sessionId); 152 if (session == null) { 153 throw new IllegalStateException("Missing session " + sessionId); 154 } 155 if (Binder.getCallingUid() != session.installerUid) { 156 throw new SecurityException("Caller has no access to session " + sessionId); 157 } 158 return session; 159 } 160 } 161 162 private int allocateSessionIdLocked() { 163 if (mSessions.get(mNextSessionId) != null) { 164 throw new IllegalStateException("Next session already allocated"); 165 } 166 return mNextSessionId++; 167 } 168 169 @Override 170 public int[] getSessions(int userId, String installerPackageName) { 171 final int callingUid = Binder.getCallingUid(); 172 mPm.enforceCrossUserPermission(callingUid, userId, false, TAG); 173 mAppOps.checkPackage(callingUid, installerPackageName); 174 175 int[] matching = new int[0]; 176 synchronized (mSessions) { 177 for (int i = 0; i < mSessions.size(); i++) { 178 final int key = mSessions.keyAt(i); 179 final PackageInstallerSession session = mSessions.valueAt(i); 180 if (session.userId == userId 181 && session.installerPackageName.equals(installerPackageName)) { 182 matching = ArrayUtils.appendInt(matching, key); 183 } 184 } 185 } 186 return matching; 187 } 188 189 @Override 190 public void uninstall(int userId, String basePackageName, IPackageDeleteObserver observer) { 191 mPm.deletePackageAsUser(basePackageName, observer, userId, 0); 192 } 193 194 @Override 195 public void uninstallSplit(int userId, String basePackageName, String overlayName, 196 IPackageDeleteObserver observer) { 197 // TODO: flesh out once PM has split support 198 throw new UnsupportedOperationException(); 199 } 200 201 class Callback { 202 public void onProgressChanged(PackageInstallerSession session) { 203 // TODO: notify listeners 204 } 205 206 public void onSessionInvalid(PackageInstallerSession session) { 207 writeSessionsAsync(); 208 } 209 } 210} 211