blob: 276687f844da18c0424dcf2ccfcd1bb8f26f4597 [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;
24import android.os.RecoverySystem;
25import android.os.RemoteException;
26import android.os.SystemProperties;
27import android.system.ErrnoException;
28import android.system.Os;
29import android.util.Slog;
30
Tao Baodd3baae2016-02-26 10:28:58 -080031import libcore.io.IoUtils;
32
33import java.io.DataInputStream;
34import java.io.DataOutputStream;
Tao Baoe8a403d2015-12-31 07:44:55 -080035import java.io.File;
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
53 private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
Tao Baoe8a403d2015-12-31 07:44:55 -080054
55 private Context mContext;
56
57 public RecoverySystemService(Context context) {
58 super(context);
59 mContext = context;
60 }
61
62 @Override
63 public void onStart() {
64 publishBinderService(Context.RECOVERY_SERVICE, new BinderService());
65 }
66
67 private final class BinderService extends IRecoverySystem.Stub {
68 @Override // Binder call
69 public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) {
70 if (DEBUG) Slog.d(TAG, "uncrypt: " + filename);
71
72 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
73
74 // Write the filename into UNCRYPT_PACKAGE_FILE to be read by
75 // uncrypt.
76 RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();
77
78 try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) {
79 uncryptFile.write(filename + "\n");
80 } catch (IOException e) {
81 Slog.e(TAG, "IOException when writing \"" + RecoverySystem.UNCRYPT_PACKAGE_FILE +
Tao Bao12844822016-03-22 10:42:32 -070082 "\": ", e);
Tao Baoe8a403d2015-12-31 07:44:55 -080083 return false;
84 }
85
Tao Baoe8a403d2015-12-31 07:44:55 -080086 // Trigger uncrypt via init.
87 SystemProperties.set("ctl.start", "uncrypt");
88
Tao Baodd3baae2016-02-26 10:28:58 -080089 // Connect to the uncrypt service socket.
90 LocalSocket socket = connectService();
91 if (socket == null) {
92 Slog.e(TAG, "Failed to connect to uncrypt socket");
93 return false;
94 }
95
96 // Read the status from the socket.
Tao Bao12844822016-03-22 10:42:32 -070097 DataInputStream dis = null;
98 DataOutputStream dos = null;
99 try {
100 dis = new DataInputStream(socket.getInputStream());
101 dos = new DataOutputStream(socket.getOutputStream());
Tao Baoe8a403d2015-12-31 07:44:55 -0800102 int lastStatus = Integer.MIN_VALUE;
103 while (true) {
Tao Baodd3baae2016-02-26 10:28:58 -0800104 int status = dis.readInt();
105 // Avoid flooding the log with the same message.
106 if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
107 continue;
108 }
109 lastStatus = status;
Tao Baoe8a403d2015-12-31 07:44:55 -0800110
Tao Baodd3baae2016-02-26 10:28:58 -0800111 if (status >= 0 && status <= 100) {
112 // Update status
113 Slog.i(TAG, "uncrypt read status: " + status);
114 if (listener != null) {
115 try {
116 listener.onProgress(status);
Tao Bao12844822016-03-22 10:42:32 -0700117 } catch (RemoteException ignored) {
Tao Baodd3baae2016-02-26 10:28:58 -0800118 Slog.w(TAG, "RemoteException when posting progress");
Tao Baoe8a403d2015-12-31 07:44:55 -0800119 }
Tao Baoe8a403d2015-12-31 07:44:55 -0800120 }
Tao Baodd3baae2016-02-26 10:28:58 -0800121 if (status == 100) {
122 Slog.i(TAG, "uncrypt successfully finished.");
123 // Ack receipt of the final status code. uncrypt
124 // waits for the ack so the socket won't be
125 // destroyed before we receive the code.
126 dos.writeInt(0);
Tao Baodd3baae2016-02-26 10:28:58 -0800127 break;
128 }
129 } else {
130 // Error in /system/bin/uncrypt.
131 Slog.e(TAG, "uncrypt failed with status: " + status);
132 // Ack receipt of the final status code. uncrypt waits
133 // for the ack so the socket won't be destroyed before
134 // we receive the code.
135 dos.writeInt(0);
Tao Baoe8a403d2015-12-31 07:44:55 -0800136 return false;
137 }
138 }
Tao Baodd3baae2016-02-26 10:28:58 -0800139 } catch (IOException e) {
Tao Bao12844822016-03-22 10:42:32 -0700140 Slog.e(TAG, "IOException when reading status: ", e);
Tao Baoe8a403d2015-12-31 07:44:55 -0800141 return false;
Tao Baodd3baae2016-02-26 10:28:58 -0800142 } finally {
Tao Bao12844822016-03-22 10:42:32 -0700143 IoUtils.closeQuietly(dis);
144 IoUtils.closeQuietly(dos);
Tao Baodd3baae2016-02-26 10:28:58 -0800145 IoUtils.closeQuietly(socket);
Tao Baoe8a403d2015-12-31 07:44:55 -0800146 }
147
148 return true;
149 }
150
151 @Override // Binder call
152 public boolean clearBcb() {
153 if (DEBUG) Slog.d(TAG, "clearBcb");
154 return setupOrClearBcb(false, null);
155 }
156
157 @Override // Binder call
158 public boolean setupBcb(String command) {
159 if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]");
160 return setupOrClearBcb(true, command);
161 }
162
Tao Baodd3baae2016-02-26 10:28:58 -0800163 private LocalSocket connectService() {
164 LocalSocket socket = new LocalSocket();
165 boolean done = false;
166 // The uncrypt socket will be created by init upon receiving the
167 // service request. It may not be ready by this point. So we will
168 // keep retrying until success or reaching timeout.
169 for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
170 try {
171 socket.connect(new LocalSocketAddress(UNCRYPT_SOCKET,
172 LocalSocketAddress.Namespace.RESERVED));
173 done = true;
174 break;
Tao Bao12844822016-03-22 10:42:32 -0700175 } catch (IOException ignored) {
Tao Baodd3baae2016-02-26 10:28:58 -0800176 try {
177 Thread.sleep(1000);
178 } catch (InterruptedException e) {
Tao Bao12844822016-03-22 10:42:32 -0700179 Slog.w(TAG, "Interrupted: ", e);
Tao Baodd3baae2016-02-26 10:28:58 -0800180 }
Tao Baoe8a403d2015-12-31 07:44:55 -0800181 }
182 }
Tao Baodd3baae2016-02-26 10:28:58 -0800183 if (!done) {
184 Slog.e(TAG, "Timed out connecting to uncrypt socket");
185 return null;
Tao Baoe8a403d2015-12-31 07:44:55 -0800186 }
Tao Baodd3baae2016-02-26 10:28:58 -0800187 return socket;
188 }
189
190 private boolean setupOrClearBcb(boolean isSetup, String command) {
191 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
Tao Baoe8a403d2015-12-31 07:44:55 -0800192
193 if (isSetup) {
194 SystemProperties.set("ctl.start", "setup-bcb");
195 } else {
196 SystemProperties.set("ctl.start", "clear-bcb");
197 }
198
Tao Baodd3baae2016-02-26 10:28:58 -0800199 // Connect to the uncrypt service socket.
200 LocalSocket socket = connectService();
201 if (socket == null) {
202 Slog.e(TAG, "Failed to connect to uncrypt socket");
Tao Baoe8a403d2015-12-31 07:44:55 -0800203 return false;
204 }
205
Tao Bao12844822016-03-22 10:42:32 -0700206 DataInputStream dis = null;
207 DataOutputStream dos = null;
208 try {
209 dis = new DataInputStream(socket.getInputStream());
210 dos = new DataOutputStream(socket.getOutputStream());
211
Tao Baodd3baae2016-02-26 10:28:58 -0800212 // Send the BCB commands if it's to setup BCB.
213 if (isSetup) {
214 dos.writeInt(command.length());
215 dos.writeBytes(command);
216 dos.flush();
217 }
218
219 // Read the status from the socket.
220 int status = dis.readInt();
221
222 // Ack receipt of the status code. uncrypt waits for the ack so
223 // the socket won't be destroyed before we receive the code.
224 dos.writeInt(0);
Tao Baodd3baae2016-02-26 10:28:58 -0800225
226 if (status == 100) {
227 Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") +
228 " bcb successfully finished.");
229 } else {
230 // Error in /system/bin/uncrypt.
231 Slog.e(TAG, "uncrypt failed with status: " + status);
232 return false;
233 }
234 } catch (IOException e) {
Tao Bao12844822016-03-22 10:42:32 -0700235 Slog.e(TAG, "IOException when communicating with uncrypt: ", e);
Tao Baodd3baae2016-02-26 10:28:58 -0800236 return false;
237 } finally {
Tao Bao12844822016-03-22 10:42:32 -0700238 IoUtils.closeQuietly(dis);
239 IoUtils.closeQuietly(dos);
Tao Baodd3baae2016-02-26 10:28:58 -0800240 IoUtils.closeQuietly(socket);
241 }
242
Tao Baoe8a403d2015-12-31 07:44:55 -0800243 return true;
244 }
245 }
246}