blob: 1517887efec2ac960022730889a725012cc8c3d4 [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
17package com.android.server;
18
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;
28import android.system.ErrnoException;
29import android.system.Os;
30import android.util.Slog;
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.File;
Tao Baoe8a403d2015-12-31 07:44:55 -080037import java.io.FileWriter;
38import java.io.IOException;
39
40/**
41 * The recovery system service is responsible for coordinating recovery related
42 * functions on the device. It sets up (or clears) the bootloader control block
43 * (BCB), which will be read by the bootloader and the recovery image. It also
44 * triggers /system/bin/uncrypt via init to de-encrypt an OTA package on the
45 * /data partition so that it can be accessed under the recovery image.
46 */
47public final class RecoverySystemService extends SystemService {
48 private static final String TAG = "RecoverySystemService";
49 private static final boolean DEBUG = false;
50
Tao Baodd3baae2016-02-26 10:28:58 -080051 // The socket at /dev/socket/uncrypt to communicate with uncrypt.
52 private static final String UNCRYPT_SOCKET = "uncrypt";
53
Tao Bao794c8b02016-09-27 11:15:42 -070054 // The init services that communicate with /system/bin/uncrypt.
55 private static final String INIT_SERVICE_UNCRYPT = "init.svc.uncrypt";
56 private static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb";
57 private static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb";
58
Tao Baodd3baae2016-02-26 10:28:58 -080059 private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
Tao Baoe8a403d2015-12-31 07:44:55 -080060
Tao Bao794c8b02016-09-27 11:15:42 -070061 private static final Object sRequestLock = new Object();
62
Tao Baoe8a403d2015-12-31 07:44:55 -080063 private Context mContext;
64
65 public RecoverySystemService(Context context) {
66 super(context);
67 mContext = context;
68 }
69
70 @Override
71 public void onStart() {
72 publishBinderService(Context.RECOVERY_SERVICE, new BinderService());
73 }
74
75 private final class BinderService extends IRecoverySystem.Stub {
76 @Override // Binder call
77 public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) {
78 if (DEBUG) Slog.d(TAG, "uncrypt: " + filename);
79
Tao Bao794c8b02016-09-27 11:15:42 -070080 synchronized (sRequestLock) {
81 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
Tao Baoe8a403d2015-12-31 07:44:55 -080082
Tao Bao794c8b02016-09-27 11:15:42 -070083 final boolean available = checkAndWaitForUncryptService();
84 if (!available) {
85 Slog.e(TAG, "uncrypt service is unavailable.");
86 return false;
Tao Baoe8a403d2015-12-31 07:44:55 -080087 }
Tao Baoe8a403d2015-12-31 07:44:55 -080088
Tao Bao794c8b02016-09-27 11:15:42 -070089 // Write the filename into UNCRYPT_PACKAGE_FILE to be read by
90 // uncrypt.
91 RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();
92
93 try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) {
94 uncryptFile.write(filename + "\n");
95 } catch (IOException e) {
96 Slog.e(TAG, "IOException when writing \"" +
97 RecoverySystem.UNCRYPT_PACKAGE_FILE + "\":", e);
98 return false;
99 }
100
101 // Trigger uncrypt via init.
102 SystemProperties.set("ctl.start", "uncrypt");
103
104 // Connect to the uncrypt service socket.
105 LocalSocket socket = connectService();
106 if (socket == null) {
107 Slog.e(TAG, "Failed to connect to uncrypt socket");
108 return false;
109 }
110
111 // Read the status from the socket.
112 DataInputStream dis = null;
113 DataOutputStream dos = null;
114 try {
115 dis = new DataInputStream(socket.getInputStream());
116 dos = new DataOutputStream(socket.getOutputStream());
117 int lastStatus = Integer.MIN_VALUE;
118 while (true) {
119 int status = dis.readInt();
120 // Avoid flooding the log with the same message.
121 if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
122 continue;
123 }
124 lastStatus = status;
125
126 if (status >= 0 && status <= 100) {
127 // Update status
128 Slog.i(TAG, "uncrypt read status: " + status);
129 if (listener != null) {
130 try {
131 listener.onProgress(status);
132 } catch (RemoteException ignored) {
133 Slog.w(TAG, "RemoteException when posting progress");
134 }
135 }
136 if (status == 100) {
137 Slog.i(TAG, "uncrypt successfully finished.");
138 // Ack receipt of the final status code. uncrypt
139 // waits for the ack so the socket won't be
140 // destroyed before we receive the code.
141 dos.writeInt(0);
142 break;
143 }
144 } else {
145 // Error in /system/bin/uncrypt.
146 Slog.e(TAG, "uncrypt failed with status: " + status);
147 // Ack receipt of the final status code. uncrypt waits
148 // for the ack so the socket won't be destroyed before
149 // we receive the code.
150 dos.writeInt(0);
151 return false;
152 }
153 }
154 } catch (IOException e) {
155 Slog.e(TAG, "IOException when reading status: ", e);
156 return false;
157 } finally {
158 IoUtils.closeQuietly(dis);
159 IoUtils.closeQuietly(dos);
160 IoUtils.closeQuietly(socket);
161 }
162
163 return true;
164 }
Tao Baoe8a403d2015-12-31 07:44:55 -0800165 }
166
167 @Override // Binder call
168 public boolean clearBcb() {
169 if (DEBUG) Slog.d(TAG, "clearBcb");
Tao Bao794c8b02016-09-27 11:15:42 -0700170 synchronized (sRequestLock) {
171 return setupOrClearBcb(false, null);
172 }
Tao Baoe8a403d2015-12-31 07:44:55 -0800173 }
174
175 @Override // Binder call
176 public boolean setupBcb(String command) {
177 if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]");
Tao Bao794c8b02016-09-27 11:15:42 -0700178 synchronized (sRequestLock) {
179 return setupOrClearBcb(true, command);
180 }
181 }
182
183 @Override // Binder call
Tao Baocc769912017-01-17 12:42:43 -0800184 public void rebootRecoveryWithCommand(String command) {
Tao Bao794c8b02016-09-27 11:15:42 -0700185 if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
186 synchronized (sRequestLock) {
187 if (!setupOrClearBcb(true, command)) {
188 return;
189 }
190
191 // Having set up the BCB, go ahead and reboot.
192 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
Tao Baocc769912017-01-17 12:42:43 -0800193 pm.reboot(PowerManager.REBOOT_RECOVERY);
Tao Bao794c8b02016-09-27 11:15:42 -0700194 }
195 }
196
197 /**
198 * Check if any of the init services is still running. If so, we cannot
199 * start a new uncrypt/setup-bcb/clear-bcb service right away; otherwise
200 * it may break the socket communication since init creates / deletes
201 * the socket (/dev/socket/uncrypt) on service start / exit.
202 */
203 private boolean checkAndWaitForUncryptService() {
204 for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
205 final String uncryptService = SystemProperties.get(INIT_SERVICE_UNCRYPT);
206 final String setupBcbService = SystemProperties.get(INIT_SERVICE_SETUP_BCB);
207 final String clearBcbService = SystemProperties.get(INIT_SERVICE_CLEAR_BCB);
208 final boolean busy = "running".equals(uncryptService) ||
209 "running".equals(setupBcbService) || "running".equals(clearBcbService);
210 if (DEBUG) {
211 Slog.i(TAG, "retry: " + retry + " busy: " + busy +
212 " uncrypt: [" + uncryptService + "]" +
213 " setupBcb: [" + setupBcbService + "]" +
214 " clearBcb: [" + clearBcbService + "]");
215 }
216
217 if (!busy) {
218 return true;
219 }
220
221 try {
222 Thread.sleep(1000);
223 } catch (InterruptedException e) {
224 Slog.w(TAG, "Interrupted:", e);
225 }
226 }
227
228 return false;
Tao Baoe8a403d2015-12-31 07:44:55 -0800229 }
230
Tao Baodd3baae2016-02-26 10:28:58 -0800231 private LocalSocket connectService() {
232 LocalSocket socket = new LocalSocket();
233 boolean done = false;
234 // The uncrypt socket will be created by init upon receiving the
235 // service request. It may not be ready by this point. So we will
236 // keep retrying until success or reaching timeout.
237 for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
238 try {
239 socket.connect(new LocalSocketAddress(UNCRYPT_SOCKET,
240 LocalSocketAddress.Namespace.RESERVED));
241 done = true;
242 break;
Tao Bao12844822016-03-22 10:42:32 -0700243 } catch (IOException ignored) {
Tao Baodd3baae2016-02-26 10:28:58 -0800244 try {
245 Thread.sleep(1000);
246 } catch (InterruptedException e) {
Tao Bao794c8b02016-09-27 11:15:42 -0700247 Slog.w(TAG, "Interrupted:", e);
Tao Baodd3baae2016-02-26 10:28:58 -0800248 }
Tao Baoe8a403d2015-12-31 07:44:55 -0800249 }
250 }
Tao Baodd3baae2016-02-26 10:28:58 -0800251 if (!done) {
252 Slog.e(TAG, "Timed out connecting to uncrypt socket");
253 return null;
Tao Baoe8a403d2015-12-31 07:44:55 -0800254 }
Tao Baodd3baae2016-02-26 10:28:58 -0800255 return socket;
256 }
257
258 private boolean setupOrClearBcb(boolean isSetup, String command) {
259 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
Tao Baoe8a403d2015-12-31 07:44:55 -0800260
Tao Bao794c8b02016-09-27 11:15:42 -0700261 final boolean available = checkAndWaitForUncryptService();
262 if (!available) {
263 Slog.e(TAG, "uncrypt service is unavailable.");
264 return false;
265 }
266
Tao Baoe8a403d2015-12-31 07:44:55 -0800267 if (isSetup) {
268 SystemProperties.set("ctl.start", "setup-bcb");
269 } else {
270 SystemProperties.set("ctl.start", "clear-bcb");
271 }
272
Tao Baodd3baae2016-02-26 10:28:58 -0800273 // Connect to the uncrypt service socket.
274 LocalSocket socket = connectService();
275 if (socket == null) {
276 Slog.e(TAG, "Failed to connect to uncrypt socket");
Tao Baoe8a403d2015-12-31 07:44:55 -0800277 return false;
278 }
279
Tao Bao12844822016-03-22 10:42:32 -0700280 DataInputStream dis = null;
281 DataOutputStream dos = null;
282 try {
283 dis = new DataInputStream(socket.getInputStream());
284 dos = new DataOutputStream(socket.getOutputStream());
285
Tao Baodd3baae2016-02-26 10:28:58 -0800286 // Send the BCB commands if it's to setup BCB.
287 if (isSetup) {
aquanoxb80c3cd2017-09-02 01:49:50 +0800288 byte[] cmdUtf8 = command.getBytes("UTF-8");
289 dos.writeInt(cmdUtf8.length);
290 dos.write(cmdUtf8, 0, cmdUtf8.length);
Tao Baodd3baae2016-02-26 10:28:58 -0800291 dos.flush();
292 }
293
294 // Read the status from the socket.
295 int status = dis.readInt();
296
297 // Ack receipt of the status code. uncrypt waits for the ack so
298 // the socket won't be destroyed before we receive the code.
299 dos.writeInt(0);
Tao Baodd3baae2016-02-26 10:28:58 -0800300
301 if (status == 100) {
302 Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") +
303 " bcb successfully finished.");
304 } else {
305 // Error in /system/bin/uncrypt.
306 Slog.e(TAG, "uncrypt failed with status: " + status);
307 return false;
308 }
309 } catch (IOException e) {
Tao Bao794c8b02016-09-27 11:15:42 -0700310 Slog.e(TAG, "IOException when communicating with uncrypt:", e);
Tao Baodd3baae2016-02-26 10:28:58 -0800311 return false;
312 } finally {
Tao Bao12844822016-03-22 10:42:32 -0700313 IoUtils.closeQuietly(dis);
314 IoUtils.closeQuietly(dos);
Tao Baodd3baae2016-02-26 10:28:58 -0800315 IoUtils.closeQuietly(socket);
316 }
317
Tao Baoe8a403d2015-12-31 07:44:55 -0800318 return true;
319 }
320 }
321}