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