blob: d284d07b6b924340a5c210275902fdc4c68f37c0 [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 +
82 "\": " + e.getMessage());
83 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.
97 try (DataInputStream dis = new DataInputStream(socket.getInputStream());
98 DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
Tao Baoe8a403d2015-12-31 07:44:55 -080099 int lastStatus = Integer.MIN_VALUE;
100 while (true) {
Tao Baodd3baae2016-02-26 10:28:58 -0800101 int status = dis.readInt();
102 // Avoid flooding the log with the same message.
103 if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
104 continue;
105 }
106 lastStatus = status;
Tao Baoe8a403d2015-12-31 07:44:55 -0800107
Tao Baodd3baae2016-02-26 10:28:58 -0800108 if (status >= 0 && status <= 100) {
109 // Update status
110 Slog.i(TAG, "uncrypt read status: " + status);
111 if (listener != null) {
112 try {
113 listener.onProgress(status);
114 } catch (RemoteException unused) {
115 Slog.w(TAG, "RemoteException when posting progress");
Tao Baoe8a403d2015-12-31 07:44:55 -0800116 }
Tao Baoe8a403d2015-12-31 07:44:55 -0800117 }
Tao Baodd3baae2016-02-26 10:28:58 -0800118 if (status == 100) {
119 Slog.i(TAG, "uncrypt successfully finished.");
120 // Ack receipt of the final status code. uncrypt
121 // waits for the ack so the socket won't be
122 // destroyed before we receive the code.
123 dos.writeInt(0);
124 dos.flush();
125 break;
126 }
127 } else {
128 // Error in /system/bin/uncrypt.
129 Slog.e(TAG, "uncrypt failed with status: " + status);
130 // Ack receipt of the final status code. uncrypt waits
131 // for the ack so the socket won't be destroyed before
132 // we receive the code.
133 dos.writeInt(0);
134 dos.flush();
Tao Baoe8a403d2015-12-31 07:44:55 -0800135 return false;
136 }
137 }
Tao Baodd3baae2016-02-26 10:28:58 -0800138 } catch (IOException e) {
139 Slog.e(TAG, "IOException when reading status: " + e);
Tao Baoe8a403d2015-12-31 07:44:55 -0800140 return false;
Tao Baodd3baae2016-02-26 10:28:58 -0800141 } finally {
142 IoUtils.closeQuietly(socket);
Tao Baoe8a403d2015-12-31 07:44:55 -0800143 }
144
145 return true;
146 }
147
148 @Override // Binder call
149 public boolean clearBcb() {
150 if (DEBUG) Slog.d(TAG, "clearBcb");
151 return setupOrClearBcb(false, null);
152 }
153
154 @Override // Binder call
155 public boolean setupBcb(String command) {
156 if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]");
157 return setupOrClearBcb(true, command);
158 }
159
Tao Baodd3baae2016-02-26 10:28:58 -0800160 private LocalSocket connectService() {
161 LocalSocket socket = new LocalSocket();
162 boolean done = false;
163 // The uncrypt socket will be created by init upon receiving the
164 // service request. It may not be ready by this point. So we will
165 // keep retrying until success or reaching timeout.
166 for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
167 try {
168 socket.connect(new LocalSocketAddress(UNCRYPT_SOCKET,
169 LocalSocketAddress.Namespace.RESERVED));
170 done = true;
171 break;
172 } catch (IOException unused) {
173 try {
174 Thread.sleep(1000);
175 } catch (InterruptedException e) {
176 Slog.w(TAG, "Interrupted: " + e);
177 }
Tao Baoe8a403d2015-12-31 07:44:55 -0800178 }
179 }
Tao Baodd3baae2016-02-26 10:28:58 -0800180 if (!done) {
181 Slog.e(TAG, "Timed out connecting to uncrypt socket");
182 return null;
Tao Baoe8a403d2015-12-31 07:44:55 -0800183 }
Tao Baodd3baae2016-02-26 10:28:58 -0800184 return socket;
185 }
186
187 private boolean setupOrClearBcb(boolean isSetup, String command) {
188 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
Tao Baoe8a403d2015-12-31 07:44:55 -0800189
190 if (isSetup) {
191 SystemProperties.set("ctl.start", "setup-bcb");
192 } else {
193 SystemProperties.set("ctl.start", "clear-bcb");
194 }
195
Tao Baodd3baae2016-02-26 10:28:58 -0800196 // Connect to the uncrypt service socket.
197 LocalSocket socket = connectService();
198 if (socket == null) {
199 Slog.e(TAG, "Failed to connect to uncrypt socket");
Tao Baoe8a403d2015-12-31 07:44:55 -0800200 return false;
201 }
202
Tao Baodd3baae2016-02-26 10:28:58 -0800203 try (DataInputStream dis = new DataInputStream(socket.getInputStream());
204 DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
205 // Send the BCB commands if it's to setup BCB.
206 if (isSetup) {
207 dos.writeInt(command.length());
208 dos.writeBytes(command);
209 dos.flush();
210 }
211
212 // Read the status from the socket.
213 int status = dis.readInt();
214
215 // Ack receipt of the status code. uncrypt waits for the ack so
216 // the socket won't be destroyed before we receive the code.
217 dos.writeInt(0);
218 dos.flush();
219
220 if (status == 100) {
221 Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") +
222 " bcb successfully finished.");
223 } else {
224 // Error in /system/bin/uncrypt.
225 Slog.e(TAG, "uncrypt failed with status: " + status);
226 return false;
227 }
228 } catch (IOException e) {
229 Slog.e(TAG, "IOException when getting output stream: " + e);
230 return false;
231 } finally {
232 IoUtils.closeQuietly(socket);
233 }
234
Tao Baoe8a403d2015-12-31 07:44:55 -0800235 return true;
236 }
237 }
238}