blob: 9fa7a6646c9c0379ea90892e35126d82a6076daf [file] [log] [blame]
Christopher Tate4a627c72011-04-01 14:43:32 -07001/*
2 * Copyright (C) 2011 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.backupconfirm;
18
19import android.app.Activity;
20import android.app.backup.FullBackup;
21import android.app.backup.IBackupManager;
22import android.app.backup.IFullBackupRestoreObserver;
23import android.content.Context;
24import android.content.Intent;
25import android.os.Bundle;
26import android.os.Handler;
Christopher Tate4a627c72011-04-01 14:43:32 -070027import android.os.Message;
28import android.os.RemoteException;
29import android.os.ServiceManager;
Sudheer Shanka2250d562016-11-07 15:41:02 -080030import android.os.storage.IStorageManager;
Paul Lawrence32d06732014-08-21 08:43:41 -070031import android.os.storage.StorageManager;
Christopher Tate14d0e1a2015-01-27 15:33:40 -080032import android.text.Editable;
33import android.text.TextWatcher;
Christopher Tate4a627c72011-04-01 14:43:32 -070034import android.util.Slog;
35import android.view.View;
36import android.widget.Button;
37import android.widget.TextView;
38import android.widget.Toast;
39
40/**
41 * Confirm with the user that a requested full backup/restore operation is legitimate.
42 * Any attempt to perform a full backup/restore will launch this UI and wait for a
43 * designated timeout interval (nominally 30 seconds) for the user to confirm. If the
44 * user fails to respond within the timeout period, or explicitly refuses the operation
45 * within the UI presented here, no data will be transferred off the device.
46 *
47 * Note that the fully scoped name of this class is baked into the backup manager service.
48 *
49 * @hide
50 */
51public class BackupRestoreConfirmation extends Activity {
52 static final String TAG = "BackupRestoreConfirmation";
53 static final boolean DEBUG = true;
54
Christopher Tate3851fa92011-08-05 12:28:15 -070055 static final String DID_ACKNOWLEDGE = "did_acknowledge";
56
Christopher Tate4a627c72011-04-01 14:43:32 -070057 static final int MSG_START_BACKUP = 1;
58 static final int MSG_BACKUP_PACKAGE = 2;
59 static final int MSG_END_BACKUP = 3;
60 static final int MSG_START_RESTORE = 11;
61 static final int MSG_RESTORE_PACKAGE = 12;
62 static final int MSG_END_RESTORE = 13;
63 static final int MSG_TIMEOUT = 100;
64
65 Handler mHandler;
66 IBackupManager mBackupManager;
Sudheer Shanka2250d562016-11-07 15:41:02 -080067 IStorageManager mStorageManager;
Christopher Tate4a627c72011-04-01 14:43:32 -070068 FullObserver mObserver;
69 int mToken;
Christopher Tate32418be2011-10-10 13:51:12 -070070 boolean mIsEncrypted;
Christopher Tateec5d4a02011-06-07 15:35:45 -070071 boolean mDidAcknowledge;
Christopher Tate4a627c72011-04-01 14:43:32 -070072
73 TextView mStatusView;
Christopher Tate728a1c42011-07-28 18:03:03 -070074 TextView mCurPassword;
75 TextView mEncPassword;
Christopher Tate4a627c72011-04-01 14:43:32 -070076 Button mAllowButton;
77 Button mDenyButton;
78
79 // Handler for dealing with observer callbacks on the main thread
80 class ObserverHandler extends Handler {
81 Context mContext;
82 ObserverHandler(Context context) {
83 mContext = context;
Christopher Tateec5d4a02011-06-07 15:35:45 -070084 mDidAcknowledge = false;
Christopher Tate4a627c72011-04-01 14:43:32 -070085 }
86
87 @Override
88 public void handleMessage(Message msg) {
89 switch (msg.what) {
90 case MSG_START_BACKUP: {
Christopher Tate28b591c2011-09-14 17:34:52 -070091 Toast.makeText(mContext, R.string.toast_backup_started, Toast.LENGTH_LONG).show();
Christopher Tate4a627c72011-04-01 14:43:32 -070092 }
93 break;
94
95 case MSG_BACKUP_PACKAGE: {
96 String name = (String) msg.obj;
97 mStatusView.setText(name);
98 }
99 break;
100
101 case MSG_END_BACKUP: {
Christopher Tate28b591c2011-09-14 17:34:52 -0700102 Toast.makeText(mContext, R.string.toast_backup_ended, Toast.LENGTH_LONG).show();
Christopher Tatedc92c822011-05-13 15:38:02 -0700103 finish();
Christopher Tate4a627c72011-04-01 14:43:32 -0700104 }
105 break;
106
107 case MSG_START_RESTORE: {
Christopher Tate28b591c2011-09-14 17:34:52 -0700108 Toast.makeText(mContext, R.string.toast_restore_started, Toast.LENGTH_LONG).show();
Christopher Tate4a627c72011-04-01 14:43:32 -0700109 }
110 break;
111
112 case MSG_RESTORE_PACKAGE: {
Christopher Tate75a99702011-05-18 16:28:19 -0700113 String name = (String) msg.obj;
114 mStatusView.setText(name);
Christopher Tate4a627c72011-04-01 14:43:32 -0700115 }
116 break;
117
118 case MSG_END_RESTORE: {
Christopher Tate28b591c2011-09-14 17:34:52 -0700119 Toast.makeText(mContext, R.string.toast_restore_ended, Toast.LENGTH_SHORT).show();
Christopher Tatedc92c822011-05-13 15:38:02 -0700120 finish();
Christopher Tate4a627c72011-04-01 14:43:32 -0700121 }
122 break;
123
124 case MSG_TIMEOUT: {
Christopher Tate28b591c2011-09-14 17:34:52 -0700125 Toast.makeText(mContext, R.string.toast_timeout, Toast.LENGTH_LONG).show();
Christopher Tate4a627c72011-04-01 14:43:32 -0700126 }
127 break;
128 }
129 }
130 }
131
132 @Override
133 public void onCreate(Bundle icicle) {
134 super.onCreate(icicle);
135
136 final Intent intent = getIntent();
137 final String action = intent.getAction();
138
Christopher Tate2efd2db2011-07-19 16:32:49 -0700139 final int layoutId;
Christopher Tatec58cf7d2011-09-13 17:51:18 -0700140 final int titleId;
Christopher Tate4a627c72011-04-01 14:43:32 -0700141 if (action.equals(FullBackup.FULL_BACKUP_INTENT_ACTION)) {
142 layoutId = R.layout.confirm_backup;
Christopher Tatec58cf7d2011-09-13 17:51:18 -0700143 titleId = R.string.backup_confirm_title;
Christopher Tate4a627c72011-04-01 14:43:32 -0700144 } else if (action.equals(FullBackup.FULL_RESTORE_INTENT_ACTION)) {
145 layoutId = R.layout.confirm_restore;
Christopher Tatec58cf7d2011-09-13 17:51:18 -0700146 titleId = R.string.restore_confirm_title;
Christopher Tate4a627c72011-04-01 14:43:32 -0700147 } else {
148 Slog.w(TAG, "Backup/restore confirmation activity launched with invalid action!");
149 finish();
150 return;
151 }
152
153 mToken = intent.getIntExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, -1);
154 if (mToken < 0) {
155 Slog.e(TAG, "Backup/restore confirmation requested but no token passed!");
156 finish();
157 return;
158 }
159
160 mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
Sudheer Shanka2250d562016-11-07 15:41:02 -0800161 mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
Christopher Tate4a627c72011-04-01 14:43:32 -0700162
163 mHandler = new ObserverHandler(getApplicationContext());
Christopher Tate3851fa92011-08-05 12:28:15 -0700164 final Object oldObserver = getLastNonConfigurationInstance();
165 if (oldObserver == null) {
166 mObserver = new FullObserver(mHandler);
167 } else {
168 mObserver = (FullObserver) oldObserver;
169 mObserver.setHandler(mHandler);
170 }
Christopher Tate4a627c72011-04-01 14:43:32 -0700171
Christopher Tatec58cf7d2011-09-13 17:51:18 -0700172 setTitle(titleId);
Christopher Tate4a627c72011-04-01 14:43:32 -0700173 setContentView(layoutId);
174
175 // Same resource IDs for each layout variant (backup / restore)
Alan Viverette51efddb2017-04-05 10:00:01 -0400176 mStatusView = findViewById(R.id.package_name);
177 mAllowButton = findViewById(R.id.button_allow);
178 mDenyButton = findViewById(R.id.button_deny);
Christopher Tate4a627c72011-04-01 14:43:32 -0700179
Alan Viverette51efddb2017-04-05 10:00:01 -0400180 mCurPassword = findViewById(R.id.password);
181 mEncPassword = findViewById(R.id.enc_password);
182 TextView curPwDesc = findViewById(R.id.password_desc);
Christopher Tate728a1c42011-07-28 18:03:03 -0700183
Christopher Tate4a627c72011-04-01 14:43:32 -0700184 mAllowButton.setOnClickListener(new View.OnClickListener() {
185 @Override
186 public void onClick(View v) {
Christopher Tateec5d4a02011-06-07 15:35:45 -0700187 sendAcknowledgement(mToken, true, mObserver);
Christopher Tate4a627c72011-04-01 14:43:32 -0700188 mAllowButton.setEnabled(false);
189 mDenyButton.setEnabled(false);
190 }
191 });
192
193 mDenyButton.setOnClickListener(new View.OnClickListener() {
194 @Override
195 public void onClick(View v) {
Christopher Tateec5d4a02011-06-07 15:35:45 -0700196 sendAcknowledgement(mToken, false, mObserver);
Christopher Tate4a627c72011-04-01 14:43:32 -0700197 mAllowButton.setEnabled(false);
198 mDenyButton.setEnabled(false);
Christopher Tate14d0e1a2015-01-27 15:33:40 -0800199 finish();
Christopher Tate4a627c72011-04-01 14:43:32 -0700200 }
201 });
Christopher Tate3851fa92011-08-05 12:28:15 -0700202
203 // if we're a relaunch we may need to adjust button enable state
204 if (icicle != null) {
205 mDidAcknowledge = icicle.getBoolean(DID_ACKNOWLEDGE, false);
206 mAllowButton.setEnabled(!mDidAcknowledge);
207 mDenyButton.setEnabled(!mDidAcknowledge);
208 }
Christopher Tate14d0e1a2015-01-27 15:33:40 -0800209
210 // We vary the password prompt depending on whether one is predefined, and whether
211 // the device is encrypted.
212 mIsEncrypted = deviceIsEncrypted();
213 if (!haveBackupPassword()) {
214 curPwDesc.setVisibility(View.GONE);
215 mCurPassword.setVisibility(View.GONE);
216 if (layoutId == R.layout.confirm_backup) {
Alan Viverette51efddb2017-04-05 10:00:01 -0400217 TextView encPwDesc = findViewById(R.id.enc_password_desc);
Christopher Tate14d0e1a2015-01-27 15:33:40 -0800218 if (mIsEncrypted) {
219 encPwDesc.setText(R.string.backup_enc_password_required);
220 monitorEncryptionPassword();
221 } else {
222 encPwDesc.setText(R.string.backup_enc_password_optional);
223 }
224 }
225 }
226 }
227
228 private void monitorEncryptionPassword() {
229 mAllowButton.setEnabled(false);
230 mEncPassword.addTextChangedListener(new TextWatcher() {
231 @Override
232 public void onTextChanged(CharSequence s, int start, int before, int count) { }
233
234 @Override
235 public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
236
237 @Override
238 public void afterTextChanged(Editable s) {
239 mAllowButton.setEnabled(mEncPassword.getText().length() > 0);
240 }
241 });
Christopher Tate3851fa92011-08-05 12:28:15 -0700242 }
243
244 // Preserve the restore observer callback binder across activity relaunch
245 @Override
246 public Object onRetainNonConfigurationInstance() {
247 return mObserver;
Christopher Tate4a627c72011-04-01 14:43:32 -0700248 }
249
250 @Override
Christopher Tate3851fa92011-08-05 12:28:15 -0700251 protected void onSaveInstanceState(Bundle outState) {
252 outState.putBoolean(DID_ACKNOWLEDGE, mDidAcknowledge);
Christopher Tate4a627c72011-04-01 14:43:32 -0700253 }
254
Christopher Tateec5d4a02011-06-07 15:35:45 -0700255 void sendAcknowledgement(int token, boolean allow, IFullBackupRestoreObserver observer) {
256 if (!mDidAcknowledge) {
257 mDidAcknowledge = true;
Christopher Tate2efd2db2011-07-19 16:32:49 -0700258
Christopher Tateec5d4a02011-06-07 15:35:45 -0700259 try {
Paul Lawrence32d06732014-08-21 08:43:41 -0700260 CharSequence encPassword = mEncPassword.getText();
Christopher Tate728a1c42011-07-28 18:03:03 -0700261 mBackupManager.acknowledgeFullBackupOrRestore(mToken,
262 allow,
263 String.valueOf(mCurPassword.getText()),
Christopher Tate32418be2011-10-10 13:51:12 -0700264 String.valueOf(encPassword),
Christopher Tate728a1c42011-07-28 18:03:03 -0700265 mObserver);
Christopher Tateec5d4a02011-06-07 15:35:45 -0700266 } catch (RemoteException e) {
267 // TODO: bail gracefully if we can't contact the backup manager
268 }
269 }
270 }
271
Christopher Tate32418be2011-10-10 13:51:12 -0700272 boolean deviceIsEncrypted() {
273 try {
Sudheer Shanka2250d562016-11-07 15:41:02 -0800274 return mStorageManager.getEncryptionState()
Sudheer Shankaf7341142016-10-18 17:15:18 -0700275 != StorageManager.ENCRYPTION_STATE_NONE
Sudheer Shanka2250d562016-11-07 15:41:02 -0800276 && mStorageManager.getPasswordType()
Paul Lawrence32d06732014-08-21 08:43:41 -0700277 != StorageManager.CRYPT_TYPE_DEFAULT;
Christopher Tate32418be2011-10-10 13:51:12 -0700278 } catch (Exception e) {
Sudheer Shanka2250d562016-11-07 15:41:02 -0800279 // If we can't talk to the storagemanager service we have a serious problem; fail
Christopher Tate32418be2011-10-10 13:51:12 -0700280 // "secure" i.e. assuming that the device is encrypted.
Sudheer Shanka2250d562016-11-07 15:41:02 -0800281 Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
Christopher Tate32418be2011-10-10 13:51:12 -0700282 return true;
283 }
284 }
285
Christopher Tate728a1c42011-07-28 18:03:03 -0700286 boolean haveBackupPassword() {
287 try {
288 return mBackupManager.hasBackupPassword();
289 } catch (RemoteException e) {
290 return true; // in the failure case, assume we need one
291 }
292 }
293
Christopher Tate4a627c72011-04-01 14:43:32 -0700294 /**
295 * The observer binder for showing backup/restore progress. This binder just bounces
296 * the notifications onto the main thread.
297 */
298 class FullObserver extends IFullBackupRestoreObserver.Stub {
Christopher Tate3851fa92011-08-05 12:28:15 -0700299 private Handler mHandler;
300
301 public FullObserver(Handler h) {
302 mHandler = h;
303 }
304
305 public void setHandler(Handler h) {
306 mHandler = h;
307 }
308
Christopher Tate4a627c72011-04-01 14:43:32 -0700309 //
310 // IFullBackupRestoreObserver implementation
311 //
312 @Override
313 public void onStartBackup() throws RemoteException {
314 mHandler.sendEmptyMessage(MSG_START_BACKUP);
315 }
316
317 @Override
318 public void onBackupPackage(String name) throws RemoteException {
319 mHandler.sendMessage(mHandler.obtainMessage(MSG_BACKUP_PACKAGE, name));
320 }
321
322 @Override
323 public void onEndBackup() throws RemoteException {
324 mHandler.sendEmptyMessage(MSG_END_BACKUP);
325 }
326
327 @Override
328 public void onStartRestore() throws RemoteException {
329 mHandler.sendEmptyMessage(MSG_START_RESTORE);
330 }
331
332 @Override
333 public void onRestorePackage(String name) throws RemoteException {
334 mHandler.sendMessage(mHandler.obtainMessage(MSG_RESTORE_PACKAGE, name));
335 }
336
337 @Override
338 public void onEndRestore() throws RemoteException {
339 mHandler.sendEmptyMessage(MSG_END_RESTORE);
340 }
341
342 @Override
343 public void onTimeout() throws RemoteException {
344 mHandler.sendEmptyMessage(MSG_TIMEOUT);
345 }
346 }
347}