blob: 6d83d70f1cb180bcf7c0840e09d8b905607510ea [file] [log] [blame]
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -08001/*
2 * Copyright (C) 2019 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 android.annotation.NonNull;
Alex Buynytskyy476138c2019-12-20 14:41:47 -080020import android.content.ComponentName;
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -080021import android.content.pm.DataLoaderParams;
22import android.content.pm.InstallationFile;
Alex Buynytskyy04f73912020-02-10 08:34:18 -080023import android.content.pm.PackageInstaller;
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -080024import android.os.ParcelFileDescriptor;
Alex Buynytskyy476138c2019-12-20 14:41:47 -080025import android.os.ShellCommand;
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -080026import android.service.dataloader.DataLoaderService;
27import android.text.TextUtils;
28import android.util.Slog;
Alex Buynytskyy476138c2019-12-20 14:41:47 -080029import android.util.SparseArray;
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -080030
31import libcore.io.IoUtils;
32
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -080033import java.io.IOException;
Alex Buynytskyy476138c2019-12-20 14:41:47 -080034import java.lang.ref.WeakReference;
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -080035import java.nio.charset.StandardCharsets;
Alex Buynytskyy476138c2019-12-20 14:41:47 -080036import java.security.SecureRandom;
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -080037import java.util.Collection;
38
39/**
40 * Callback data loader for PackageManagerShellCommand installations.
41 */
42public class PackageManagerShellCommandDataLoader extends DataLoaderService {
43 public static final String TAG = "PackageManagerShellCommandDataLoader";
44
Alex Buynytskyy476138c2019-12-20 14:41:47 -080045 private static final String PACKAGE = "android";
46 private static final String CLASS = PackageManagerShellCommandDataLoader.class.getName();
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -080047
Alex Buynytskyy476138c2019-12-20 14:41:47 -080048 static final SecureRandom sRandom = new SecureRandom();
49 static final SparseArray<WeakReference<ShellCommand>> sShellCommands = new SparseArray<>();
50
51 private static final char ARGS_DELIM = '&';
52 private static final String SHELL_COMMAND_ID_PREFIX = "shellCommandId=";
53 private static final int INVALID_SHELL_COMMAND_ID = -1;
54 private static final int TOO_MANY_PENDING_SHELL_COMMANDS = 10;
55
56 private static final String STDIN_PATH = "-";
57
Alex Buynytskyycd4d3872020-02-08 17:50:50 -080058 private static String getDataLoaderParamsArgs(ShellCommand shellCommand) {
Alex Buynytskyy04f73912020-02-10 08:34:18 -080059 nativeInitialize();
60
Alex Buynytskyy476138c2019-12-20 14:41:47 -080061 int commandId;
62 synchronized (sShellCommands) {
63 // Clean up old references.
64 for (int i = sShellCommands.size() - 1; i >= 0; i--) {
65 WeakReference<ShellCommand> oldRef = sShellCommands.valueAt(i);
66 if (oldRef.get() == null) {
67 sShellCommands.removeAt(i);
68 }
69 }
70
71 // Sanity check.
72 if (sShellCommands.size() > TOO_MANY_PENDING_SHELL_COMMANDS) {
73 Slog.e(TAG, "Too many pending shell commands: " + sShellCommands.size());
74 }
75
76 // Generate new id and put ref to the array.
77 do {
78 commandId = sRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
79 } while (sShellCommands.contains(commandId));
80
81 sShellCommands.put(commandId, new WeakReference<>(shellCommand));
82 }
83
Alex Buynytskyycd4d3872020-02-08 17:50:50 -080084 return SHELL_COMMAND_ID_PREFIX + commandId;
85 }
86
87 static DataLoaderParams getStreamingDataLoaderParams(ShellCommand shellCommand) {
88 return DataLoaderParams.forStreaming(new ComponentName(PACKAGE, CLASS),
89 getDataLoaderParamsArgs(shellCommand));
Alex Buynytskyy476138c2019-12-20 14:41:47 -080090 }
91
Alex Buynytskyy04f73912020-02-10 08:34:18 -080092 static DataLoaderParams getIncrementalDataLoaderParams(ShellCommand shellCommand) {
93 return DataLoaderParams.forIncremental(new ComponentName(PACKAGE, CLASS),
Songchun Fan82fddef2020-03-06 13:30:29 -080094 getDataLoaderParamsArgs(shellCommand));
Alex Buynytskyy04f73912020-02-10 08:34:18 -080095 }
96
Alex Buynytskyy476138c2019-12-20 14:41:47 -080097 private static int extractShellCommandId(String args) {
98 int sessionIdIdx = args.indexOf(SHELL_COMMAND_ID_PREFIX);
99 if (sessionIdIdx < 0) {
100 Slog.e(TAG, "Missing shell command id param.");
101 return INVALID_SHELL_COMMAND_ID;
102 }
103 sessionIdIdx += SHELL_COMMAND_ID_PREFIX.length();
104 int delimIdx = args.indexOf(ARGS_DELIM, sessionIdIdx);
105 try {
106 if (delimIdx < 0) {
107 return Integer.parseInt(args.substring(sessionIdIdx));
108 } else {
109 return Integer.parseInt(args.substring(sessionIdIdx, delimIdx));
110 }
111 } catch (NumberFormatException e) {
112 Slog.e(TAG, "Incorrect shell command id format.", e);
113 return INVALID_SHELL_COMMAND_ID;
114 }
115 }
116
Alex Buynytskyy04f73912020-02-10 08:34:18 -0800117 private static class DataLoader implements DataLoaderService.DataLoader {
Alex Buynytskyy476138c2019-12-20 14:41:47 -0800118 private DataLoaderParams mParams = null;
119 private FileSystemConnector mConnector = null;
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800120
121 @Override
122 public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams,
123 @NonNull FileSystemConnector connector) {
Alex Buynytskyy476138c2019-12-20 14:41:47 -0800124 mParams = dataLoaderParams;
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800125 mConnector = connector;
126 return true;
127 }
Alex Buynytskyy476138c2019-12-20 14:41:47 -0800128
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800129 @Override
shengcow0696c442020-01-14 17:58:04 -0800130 public boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles,
131 @NonNull Collection<String> removedFiles) {
Alex Buynytskyy04f73912020-02-10 08:34:18 -0800132 ShellCommand shellCommand = lookupShellCommand(mParams.getArguments());
Alex Buynytskyy476138c2019-12-20 14:41:47 -0800133 if (shellCommand == null) {
134 Slog.e(TAG, "Missing shell command.");
135 return false;
136 }
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800137 try {
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800138 for (InstallationFile file : addedFiles) {
139 String filePath = new String(file.getMetadata(), StandardCharsets.UTF_8);
Alex Buynytskyy04f73912020-02-10 08:34:18 -0800140 if (TextUtils.isEmpty(filePath) || filePath.startsWith(STDIN_PATH)) {
141 final ParcelFileDescriptor inFd = getStdInPFD(shellCommand);
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800142 mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd);
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800143 } else {
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800144 ParcelFileDescriptor incomingFd = null;
145 try {
Alex Buynytskyy04f73912020-02-10 08:34:18 -0800146 incomingFd = getLocalFile(shellCommand, filePath);
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800147 mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(),
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800148 incomingFd);
149 } finally {
150 IoUtils.closeQuietly(incomingFd);
151 }
152 }
153 }
154 return true;
155 } catch (IOException e) {
Alex Buynytskyy6bce3f62020-01-16 13:39:16 -0800156 Slog.e(TAG, "Exception while streaming files", e);
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800157 return false;
158 }
159 }
160 }
161
Alex Buynytskyy04f73912020-02-10 08:34:18 -0800162 static ShellCommand lookupShellCommand(String args) {
163 final int commandId = extractShellCommandId(args);
164 if (commandId == INVALID_SHELL_COMMAND_ID) {
165 return null;
166 }
167
168 final WeakReference<ShellCommand> shellCommandRef;
169 synchronized (sShellCommands) {
170 shellCommandRef = sShellCommands.get(commandId, null);
171 }
172 final ShellCommand shellCommand =
173 shellCommandRef != null ? shellCommandRef.get() : null;
174
175 return shellCommand;
176 }
177
178 static ParcelFileDescriptor getStdInPFD(ShellCommand shellCommand) {
179 try {
180 return ParcelFileDescriptor.dup(shellCommand.getInFileDescriptor());
181 } catch (IOException e) {
182 Slog.e(TAG, "Exception while obtaining STDIN fd", e);
183 return null;
184 }
185 }
186
187 static ParcelFileDescriptor getLocalFile(ShellCommand shellCommand, String filePath) {
188 return shellCommand.openFileForSystem(filePath, "r");
189 }
190
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800191 @Override
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800192 public DataLoaderService.DataLoader onCreateDataLoader(
193 @NonNull DataLoaderParams dataLoaderParams) {
Alex Buynytskyy04f73912020-02-10 08:34:18 -0800194 if (dataLoaderParams.getType() == PackageInstaller.DATA_LOADER_TYPE_STREAMING) {
195 // This DataLoader only supports streaming installations.
196 return new DataLoader();
197 }
198 return null;
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800199 }
Alex Buynytskyy04f73912020-02-10 08:34:18 -0800200
201 /* Native methods */
202 private static native void nativeInitialize();
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800203}