PackageInstallerService.java revision ec55ef0934b8e0d1bb705434947de817f7be57f1
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.SELinux; 34import android.os.UserHandle; 35import android.os.UserManager; 36import android.system.ErrnoException; 37import android.system.Os; 38import android.util.ArraySet; 39import android.util.Slog; 40import android.util.SparseArray; 41 42import com.android.internal.annotations.GuardedBy; 43import com.android.internal.util.ArrayUtils; 44import com.android.server.IoThread; 45import com.google.android.collect.Sets; 46 47import java.io.File; 48import java.io.FilenameFilter; 49import java.io.IOException; 50 51public class PackageInstallerService extends IPackageInstaller.Stub { 52 private static final String TAG = "PackageInstaller"; 53 54 // TODO: destroy sessions with old timestamps 55 // TODO: remove outstanding sessions when installer package goes away 56 57 private final Context mContext; 58 private final PackageManagerService mPm; 59 private final AppOpsManager mAppOps; 60 61 private final File mStagingDir; 62 private final HandlerThread mInstallThread; 63 64 private final Callback mCallback = new Callback(); 65 66 @GuardedBy("mSessions") 67 private int mNextSessionId; 68 @GuardedBy("mSessions") 69 private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>(); 70 71 private static final FilenameFilter sStageFilter = new FilenameFilter() { 72 @Override 73 public boolean accept(File dir, String name) { 74 return name.startsWith("vmdl") && name.endsWith(".tmp"); 75 } 76 }; 77 78 public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) { 79 mContext = context; 80 mPm = pm; 81 mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 82 83 mStagingDir = stagingDir; 84 85 mInstallThread = new HandlerThread(TAG); 86 mInstallThread.start(); 87 88 synchronized (mSessions) { 89 readSessionsLocked(); 90 91 // Clean up orphaned staging directories 92 final ArraySet<File> stages = Sets.newArraySet(mStagingDir.listFiles(sStageFilter)); 93 for (int i = 0; i < mSessions.size(); i++) { 94 final PackageInstallerSession session = mSessions.valueAt(i); 95 stages.remove(session.sessionStageDir); 96 } 97 for (File stage : stages) { 98 Slog.w(TAG, "Deleting orphan stage " + stage); 99 if (stage.isDirectory()) { 100 FileUtils.deleteContents(stage); 101 } 102 stage.delete(); 103 } 104 } 105 } 106 107 @Deprecated 108 public File allocateSessionDir() throws IOException { 109 synchronized (mSessions) { 110 try { 111 final int sessionId = allocateSessionIdLocked(); 112 return prepareSessionStageDir(sessionId); 113 } catch (IllegalStateException e) { 114 throw new IOException(e); 115 } 116 } 117 } 118 119 private void readSessionsLocked() { 120 // TODO: implement persisting 121 mSessions.clear(); 122 mNextSessionId = 1; 123 } 124 125 private void writeSessionsLocked() { 126 // TODO: implement persisting 127 } 128 129 private void writeSessionsAsync() { 130 IoThread.getHandler().post(new Runnable() { 131 @Override 132 public void run() { 133 synchronized (mSessions) { 134 writeSessionsLocked(); 135 } 136 } 137 }); 138 } 139 140 @Override 141 public int createSession(String installerPackageName, PackageInstallerParams params, 142 int userId) { 143 final int callingUid = Binder.getCallingUid(); 144 mPm.enforceCrossUserPermission(callingUid, userId, false, TAG); 145 146 if (mPm.isUserRestricted(UserHandle.getUserId(callingUid), 147 UserManager.DISALLOW_INSTALL_APPS)) { 148 throw new SecurityException("User restriction prevents installing"); 149 } 150 151 if ((callingUid == Process.SHELL_UID) || (callingUid == 0)) { 152 params.installFlags |= INSTALL_FROM_ADB; 153 } else { 154 mAppOps.checkPackage(callingUid, installerPackageName); 155 156 params.installFlags &= ~INSTALL_FROM_ADB; 157 params.installFlags &= ~INSTALL_ALL_USERS; 158 params.installFlags |= INSTALL_REPLACE_EXISTING; 159 } 160 161 synchronized (mSessions) { 162 final long createdMillis = System.currentTimeMillis(); 163 final int sessionId = allocateSessionIdLocked(); 164 final File sessionStageDir = prepareSessionStageDir(sessionId); 165 166 final PackageInstallerSession session = new PackageInstallerSession(mCallback, mPm, 167 sessionId, userId, installerPackageName, callingUid, params, createdMillis, 168 sessionStageDir, mInstallThread.getLooper()); 169 mSessions.put(sessionId, session); 170 171 writeSessionsAsync(); 172 return sessionId; 173 } 174 } 175 176 @Override 177 public IPackageInstallerSession openSession(int sessionId) { 178 synchronized (mSessions) { 179 final PackageInstallerSession session = mSessions.get(sessionId); 180 if (session == null) { 181 throw new IllegalStateException("Missing session " + sessionId); 182 } 183 if (Binder.getCallingUid() != session.installerUid) { 184 throw new SecurityException("Caller has no access to session " + sessionId); 185 } 186 return session; 187 } 188 } 189 190 private int allocateSessionIdLocked() { 191 if (mSessions.get(mNextSessionId) != null) { 192 throw new IllegalStateException("Next session already allocated"); 193 } 194 return mNextSessionId++; 195 } 196 197 private File prepareSessionStageDir(int sessionId) { 198 final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp"); 199 200 if (file.exists()) { 201 throw new IllegalStateException(); 202 } 203 204 try { 205 Os.mkdir(file.getAbsolutePath(), 0755); 206 Os.chmod(file.getAbsolutePath(), 0755); 207 } catch (ErrnoException e) { 208 // This purposefully throws if directory already exists 209 throw new IllegalStateException("Failed to prepare session dir", e); 210 } 211 212 if (!SELinux.restorecon(file)) { 213 throw new IllegalStateException("Failed to prepare session dir"); 214 } 215 216 return file; 217 } 218 219 @Override 220 public int[] getSessions(String installerPackageName, int userId) { 221 final int callingUid = Binder.getCallingUid(); 222 mPm.enforceCrossUserPermission(callingUid, userId, false, TAG); 223 mAppOps.checkPackage(callingUid, installerPackageName); 224 225 int[] matching = new int[0]; 226 synchronized (mSessions) { 227 for (int i = 0; i < mSessions.size(); i++) { 228 final int key = mSessions.keyAt(i); 229 final PackageInstallerSession session = mSessions.valueAt(i); 230 if (session.userId == userId 231 && session.installerPackageName.equals(installerPackageName)) { 232 matching = ArrayUtils.appendInt(matching, key); 233 } 234 } 235 } 236 return matching; 237 } 238 239 @Override 240 public void uninstall(String basePackageName, int flags, IPackageDeleteObserver observer, 241 int userId) { 242 mPm.deletePackageAsUser(basePackageName, observer, userId, flags); 243 } 244 245 @Override 246 public void uninstallSplit(String basePackageName, String overlayName, int flags, 247 IPackageDeleteObserver observer, int userId) { 248 // TODO: flesh out once PM has split support 249 throw new UnsupportedOperationException(); 250 } 251 252 class Callback { 253 public void onProgressChanged(PackageInstallerSession session) { 254 // TODO: notify listeners 255 } 256 257 public void onSessionInvalid(PackageInstallerSession session) { 258 writeSessionsAsync(); 259 } 260 } 261} 262