blob: d78aaa5f9c9d2d233f90e347068b5c8f01214ad1 [file] [log] [blame]
Tao Baoe8a403d2015-12-31 07:44:55 -08001/*
2 * Copyright (C) 2016 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
Kenny Rootf96836e2019-11-19 17:11:34 -080017package com.android.server.recoverysystem;
Tao Baoe8a403d2015-12-31 07:44:55 -080018
19import android.content.Context;
Tao Baodd3baae2016-02-26 10:28:58 -080020import android.net.LocalSocket;
21import android.net.LocalSocketAddress;
Tao Baoe8a403d2015-12-31 07:44:55 -080022import android.os.IRecoverySystem;
23import android.os.IRecoverySystemProgressListener;
Tao Bao794c8b02016-09-27 11:15:42 -070024import android.os.PowerManager;
Tao Baoe8a403d2015-12-31 07:44:55 -080025import android.os.RecoverySystem;
26import android.os.RemoteException;
27import android.os.SystemProperties;
Tao Baoe8a403d2015-12-31 07:44:55 -080028import android.util.Slog;
29
Kenny Rootf96836e2019-11-19 17:11:34 -080030import com.android.server.SystemService;
31
Tao Baodd3baae2016-02-26 10:28:58 -080032import libcore.io.IoUtils;
33
34import java.io.DataInputStream;
35import java.io.DataOutputStream;
Tao Baoe8a403d2015-12-31 07:44:55 -080036import java.io.FileWriter;
37import java.io.IOException;
38
39/**
40 * The recovery system service is responsible for coordinating recovery related
41 * functions on the device. It sets up (or clears) the bootloader control block
42 * (BCB), which will be read by the bootloader and the recovery image. It also
43 * triggers /system/bin/uncrypt via init to de-encrypt an OTA package on the
44 * /data partition so that it can be accessed under the recovery image.
45 */
46public final class RecoverySystemService extends SystemService {
47 private static final String TAG = "RecoverySystemService";
48 private static final boolean DEBUG = false;
49
Tao Baodd3baae2016-02-26 10:28:58 -080050 // The socket at /dev/socket/uncrypt to communicate with uncrypt.
51 private static final String UNCRYPT_SOCKET = "uncrypt";
52
Tao Bao794c8b02016-09-27 11:15:42 -070053 // The init services that communicate with /system/bin/uncrypt.
54 private static final String INIT_SERVICE_UNCRYPT = "init.svc.uncrypt";
55 private static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb";
56 private static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb";
57
Tao Baodd3baae2016-02-26 10:28:58 -080058 private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
Tao Baoe8a403d2015-12-31 07:44:55 -080059
Tao Bao794c8b02016-09-27 11:15:42 -070060 private static final Object sRequestLock = new Object();
61
Tao Baoe8a403d2015-12-31 07:44:55 -080062 private Context mContext;
63
64 public RecoverySystemService(Context context) {
65 super(context);
66 mContext = context;
67 }
68
69 @Override
70 public void onStart() {
71 publishBinderService(Context.RECOVERY_SERVICE, new BinderService());
72 }
73
74 private final class BinderService extends IRecoverySystem.Stub {
75 @Override // Binder call
76 public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) {
77 if (DEBUG) Slog.d(TAG, "uncrypt: " + filename);
78
Tao Bao794c8b02016-09-27 11:15:42 -070079 synchronized (sRequestLock) {
80 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
Tao Baoe8a403d2015-12-31 07:44:55 -080081
Tao Bao794c8b02016-09-27 11:15:42 -070082 final boolean available = checkAndWaitForUncryptService();
83 if (!available) {
84 Slog.e(TAG, "uncrypt service is unavailable.");
85 return false;
Tao Baoe8a403d2015-12-31 07:44:55 -080086 }
Tao Baoe8a403d2015-12-31 07:44:55 -080087
Tao Bao794c8b02016-09-27 11:15:42 -070088 // Write the filename into UNCRYPT_PACKAGE_FILE to be read by
89 // uncrypt.
90 RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();
91
92 try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) {
93 uncryptFile.write(filename + "\n");
94 } catch (IOException e) {
95 Slog.e(TAG, "IOException when writing \"" +
96 RecoverySystem.UNCRYPT_PACKAGE_FILE + "\":", e);
97 return false;
98 }
99
100 // Trigger uncrypt via init.
101 SystemProperties.set("ctl.start", "uncrypt");
102
103 // Connect to the uncrypt service socket.
104 LocalSocket socket = connectService();
105 if (socket == null) {
106 Slog.e(TAG, "Failed to connect to uncrypt socket");
107 return false;
108 }
109
110 // Read the status from the socket.
111 DataInputStream dis = null;
112 DataOutputStream dos = null;
113 try {
114 dis = new DataInputStream(socket.getInputStream());
115 dos = new DataOutputStream(socket.getOutputStream());
116 int lastStatus = Integer.MIN_VALUE;
117 while (true) {
118 int status = dis.readInt();
119 // Avoid flooding the log with the same message.
120 if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
121 continue;
122 }
123 lastStatus = status;
124
125 if (status >= 0 && status <= 100) {
126 // Update status
127 Slog.i(TAG, "uncrypt read status: " + status);
128 if (listener != null) {
129 try {
130 listener.onProgress(status);
131 } catch (RemoteException ignored) {
132 Slog.w(TAG, "RemoteException when posting progress");
133 }
134 }
135 if (status == 100) {
136 Slog.i(TAG, "uncrypt successfully finished.");
137 // Ack receipt of the final status code. uncrypt
138 // waits for the ack so the socket won't be
139 // destroyed before we receive the code.
140 dos.writeInt(0);
141 break;
142 }
143 } else {
144 // Error in /system/bin/uncrypt.
145 Slog.e(TAG, "uncrypt failed with status: " + status);
146 // Ack receipt of the final status code. uncrypt waits
147 // for the ack so the socket won't be destroyed before
148 // we receive the code.
149 dos.writeInt(0);
150 return false;
151 }
152 }
153 } catch (IOException e) {
154 Slog.e(TAG, "IOException when reading status: ", e);
155 return false;
156 } finally {
157 IoUtils.closeQuietly(dis);
158 IoUtils.closeQuietly(dos);
159 IoUtils.closeQuietly(socket);
160 }
161
162 return true;
163 }
Tao Baoe8a403d2015-12-31 07:44:55 -0800164 }
165
166 @Override // Binder call
167 public boolean clearBcb() {
168 if (DEBUG) Slog.d(TAG, "clearBcb");
Tao Bao794c8b02016-09-27 11:15:42 -0700169 synchronized (sRequestLock) {
170 return setupOrClearBcb(false, null);
171 }
Tao Baoe8a403d2015-12-31 07:44:55 -0800172 }
173
174 @Override // Binder call
175 public boolean setupBcb(String command) {
176 if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]");
Tao Bao794c8b02016-09-27 11:15:42 -0700177 synchronized (sRequestLock) {
178 return setupOrClearBcb(true, command);
179 }
180 }
181
182 @Override // Binder call
Tao Baocc769912017-01-17 12:42:43 -0800183 public void rebootRecoveryWithCommand(String command) {
Tao Bao794c8b02016-09-27 11:15:42 -0700184 if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
185 synchronized (sRequestLock) {
186 if (!setupOrClearBcb(true, command)) {
187 return;
188 }
189
190 // Having set up the BCB, go ahead and reboot.
191 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
Tao Baocc769912017-01-17 12:42:43 -0800192 pm.reboot(PowerManager.REBOOT_RECOVERY);
Tao Bao794c8b02016-09-27 11:15:42 -0700193 }
194 }
195
196 /**
197 * Check if any of the init services is still running. If so, we cannot
198 * start a new uncrypt/setup-bcb/clear-bcb service right away; otherwise
199 * it may break the socket communication since init creates / deletes
200 * the socket (/dev/socket/uncrypt) on service start / exit.
201 */
202 private boolean checkAndWaitForUncryptService() {
203 for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
204 final String uncryptService = SystemProperties.get(INIT_SERVICE_UNCRYPT);
205 final String setupBcbService = SystemProperties.get(INIT_SERVICE_SETUP_BCB);
206 final String clearBcbService = SystemProperties.get(INIT_SERVICE_CLEAR_BCB);
207 final boolean busy = "running".equals(uncryptService) ||
208 "running".equals(setupBcbService) || "running".equals(clearBcbService);
209 if (DEBUG) {
210 Slog.i(TAG, "retry: " + retry + " busy: " + busy +
211 " uncrypt: [" + uncryptService + "]" +
212 " setupBcb: [" + setupBcbService + "]" +
213 " clearBcb: [" + clearBcbService + "]");
214 }
215
216 if (!busy) {
217 return true;
218 }
219
220 try {
221 Thread.sleep(1000);
222 } catch (InterruptedException e) {
223 Slog.w(TAG, "Interrupted:", e);
224 }
225 }
226
227 return false;
Tao Baoe8a403d2015-12-31 07:44:55 -0800228 }
229
Tao Baodd3baae2016-02-26 10:28:58 -0800230 private LocalSocket connectService() {
231 LocalSocket socket = new LocalSocket();
232 boolean done = false;
233 // The uncrypt socket will be created by init upon receiving the
234 // service request. It may not be ready by this point. So we will
235 // keep retrying until success or reaching timeout.
236 for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
237 try {
238 socket.connect(new LocalSocketAddress(UNCRYPT_SOCKET,
239 LocalSocketAddress.Namespace.RESERVED));
240 done = true;
241 break;
Tao Bao12844822016-03-22 10:42:32 -0700242 } catch (IOException ignored) {
Tao Baodd3baae2016-02-26 10:28:58 -0800243 try {
244 Thread.sleep(1000);
245 } catch (InterruptedException e) {
Tao Bao794c8b02016-09-27 11:15:42 -0700246 Slog.w(TAG, "Interrupted:", e);
Tao Baodd3baae2016-02-26 10:28:58 -0800247 }
Tao Baoe8a403d2015-12-31 07:44:55 -0800248 }
249 }
Tao Baodd3baae2016-02-26 10:28:58 -0800250 if (!done) {
251 Slog.e(TAG, "Timed out connecting to uncrypt socket");
252 return null;
Tao Baoe8a403d2015-12-31 07:44:55 -0800253 }
Tao Baodd3baae2016-02-26 10:28:58 -0800254 return socket;
255 }
256
257 private boolean setupOrClearBcb(boolean isSetup, String command) {
258 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
Tao Baoe8a403d2015-12-31 07:44:55 -0800259
Tao Bao794c8b02016-09-27 11:15:42 -0700260 final boolean available = checkAndWaitForUncryptService();
261 if (!available) {
262 Slog.e(TAG, "uncrypt service is unavailable.");
263 return false;
264 }
265
Tao Baoe8a403d2015-12-31 07:44:55 -0800266 if (isSetup) {
267 SystemProperties.set("ctl.start", "setup-bcb");
268 } else {
269 SystemProperties.set("ctl.start", "clear-bcb");
270 }
271
Tao Baodd3baae2016-02-26 10:28:58 -0800272 // Connect to the uncrypt service socket.
273 LocalSocket socket = connectService();
274 if (socket == null) {
275 Slog.e(TAG, "Failed to connect to uncrypt socket");
Tao Baoe8a403d2015-12-31 07:44:55 -0800276 return false;
277 }
278
Tao Bao12844822016-03-22 10:42:32 -0700279 DataInputStream dis = null;
280 DataOutputStream dos = null;
281 try {
282 dis = new DataInputStream(socket.getInputStream());
283 dos = new DataOutputStream(socket.getOutputStream());
284
Tao Baodd3baae2016-02-26 10:28:58 -0800285 // Send the BCB commands if it's to setup BCB.
286 if (isSetup) {
aquanoxb80c3cd2017-09-02 01:49:50 +0800287 byte[] cmdUtf8 = command.getBytes("UTF-8");
288 dos.writeInt(cmdUtf8.length);
289 dos.write(cmdUtf8, 0, cmdUtf8.length);
Tao Baodd3baae2016-02-26 10:28:58 -0800290 }
291
292 // Read the status from the socket.
293 int status = dis.readInt();
294
295 // Ack receipt of the status code. uncrypt waits for the ack so
296 // the socket won't be destroyed before we receive the code.
297 dos.writeInt(0);
Tao Baodd3baae2016-02-26 10:28:58 -0800298
299 if (status == 100) {
300 Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") +
301 " bcb successfully finished.");
302 } else {
303 // Error in /system/bin/uncrypt.
304 Slog.e(TAG, "uncrypt failed with status: " + status);
305 return false;
306 }
307 } catch (IOException e) {
Tao Bao794c8b02016-09-27 11:15:42 -0700308 Slog.e(TAG, "IOException when communicating with uncrypt:", e);
Tao Baodd3baae2016-02-26 10:28:58 -0800309 return false;
310 } finally {
Tao Bao12844822016-03-22 10:42:32 -0700311 IoUtils.closeQuietly(dis);
312 IoUtils.closeQuietly(dos);
Tao Baodd3baae2016-02-26 10:28:58 -0800313 IoUtils.closeQuietly(socket);
314 }
315
Tao Baoe8a403d2015-12-31 07:44:55 -0800316 return true;
317 }
318 }
319}