BackupRestoreConfirmation.java revision 3851fa9c8d180ca636e69b25f84fdcc294150009
1/* 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; 27import android.os.Message; 28import android.os.RemoteException; 29import android.os.ServiceManager; 30import android.util.Slog; 31import android.view.View; 32import android.widget.Button; 33import android.widget.TextView; 34import android.widget.Toast; 35 36/** 37 * Confirm with the user that a requested full backup/restore operation is legitimate. 38 * Any attempt to perform a full backup/restore will launch this UI and wait for a 39 * designated timeout interval (nominally 30 seconds) for the user to confirm. If the 40 * user fails to respond within the timeout period, or explicitly refuses the operation 41 * within the UI presented here, no data will be transferred off the device. 42 * 43 * Note that the fully scoped name of this class is baked into the backup manager service. 44 * 45 * @hide 46 */ 47public class BackupRestoreConfirmation extends Activity { 48 static final String TAG = "BackupRestoreConfirmation"; 49 static final boolean DEBUG = true; 50 51 static final String DID_ACKNOWLEDGE = "did_acknowledge"; 52 53 static final int MSG_START_BACKUP = 1; 54 static final int MSG_BACKUP_PACKAGE = 2; 55 static final int MSG_END_BACKUP = 3; 56 static final int MSG_START_RESTORE = 11; 57 static final int MSG_RESTORE_PACKAGE = 12; 58 static final int MSG_END_RESTORE = 13; 59 static final int MSG_TIMEOUT = 100; 60 61 Handler mHandler; 62 IBackupManager mBackupManager; 63 FullObserver mObserver; 64 int mToken; 65 boolean mDidAcknowledge; 66 67 TextView mStatusView; 68 TextView mCurPassword; 69 TextView mEncPassword; 70 Button mAllowButton; 71 Button mDenyButton; 72 73 // Handler for dealing with observer callbacks on the main thread 74 class ObserverHandler extends Handler { 75 Context mContext; 76 ObserverHandler(Context context) { 77 mContext = context; 78 mDidAcknowledge = false; 79 } 80 81 @Override 82 public void handleMessage(Message msg) { 83 switch (msg.what) { 84 case MSG_START_BACKUP: { 85 Toast.makeText(mContext, "!!! Backup starting !!!", Toast.LENGTH_LONG).show(); 86 } 87 break; 88 89 case MSG_BACKUP_PACKAGE: { 90 String name = (String) msg.obj; 91 mStatusView.setText(name); 92 } 93 break; 94 95 case MSG_END_BACKUP: { 96 Toast.makeText(mContext, "!!! Backup ended !!!", Toast.LENGTH_SHORT).show(); 97 finish(); 98 } 99 break; 100 101 case MSG_START_RESTORE: { 102 Toast.makeText(mContext, "!!! Restore starting !!!", Toast.LENGTH_LONG).show(); 103 } 104 break; 105 106 case MSG_RESTORE_PACKAGE: { 107 String name = (String) msg.obj; 108 mStatusView.setText(name); 109 } 110 break; 111 112 case MSG_END_RESTORE: { 113 Toast.makeText(mContext, "!!! Restore ended !!!", Toast.LENGTH_SHORT).show(); 114 finish(); 115 } 116 break; 117 118 case MSG_TIMEOUT: { 119 Toast.makeText(mContext, "!!! TIMED OUT !!!", Toast.LENGTH_LONG).show(); 120 } 121 break; 122 } 123 } 124 } 125 126 @Override 127 public void onCreate(Bundle icicle) { 128 super.onCreate(icicle); 129 130 final Intent intent = getIntent(); 131 final String action = intent.getAction(); 132 133 final int layoutId; 134 if (action.equals(FullBackup.FULL_BACKUP_INTENT_ACTION)) { 135 layoutId = R.layout.confirm_backup; 136 } else if (action.equals(FullBackup.FULL_RESTORE_INTENT_ACTION)) { 137 layoutId = R.layout.confirm_restore; 138 } else { 139 Slog.w(TAG, "Backup/restore confirmation activity launched with invalid action!"); 140 finish(); 141 return; 142 } 143 144 mToken = intent.getIntExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, -1); 145 if (mToken < 0) { 146 Slog.e(TAG, "Backup/restore confirmation requested but no token passed!"); 147 finish(); 148 return; 149 } 150 151 mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE)); 152 153 mHandler = new ObserverHandler(getApplicationContext()); 154 final Object oldObserver = getLastNonConfigurationInstance(); 155 if (oldObserver == null) { 156 mObserver = new FullObserver(mHandler); 157 } else { 158 mObserver = (FullObserver) oldObserver; 159 mObserver.setHandler(mHandler); 160 } 161 162 setContentView(layoutId); 163 164 // Same resource IDs for each layout variant (backup / restore) 165 mStatusView = (TextView) findViewById(R.id.package_name); 166 mAllowButton = (Button) findViewById(R.id.button_allow); 167 mDenyButton = (Button) findViewById(R.id.button_deny); 168 169 mCurPassword = (TextView) findViewById(R.id.password); 170 mEncPassword = (TextView) findViewById(R.id.enc_password); 171 TextView curPwDesc = (TextView) findViewById(R.id.password_desc); 172 173 // We vary the password prompt depending on whether one is predefined 174 if (!haveBackupPassword()) { 175 curPwDesc.setVisibility(View.GONE); 176 mCurPassword.setVisibility(View.GONE); 177 if (layoutId == R.layout.confirm_backup) { 178 TextView encPwDesc = (TextView) findViewById(R.id.enc_password_desc); 179 encPwDesc.setText(R.string.backup_enc_password_optional); 180 } 181 } 182 183 mAllowButton.setOnClickListener(new View.OnClickListener() { 184 @Override 185 public void onClick(View v) { 186 sendAcknowledgement(mToken, true, mObserver); 187 mAllowButton.setEnabled(false); 188 mDenyButton.setEnabled(false); 189 } 190 }); 191 192 mDenyButton.setOnClickListener(new View.OnClickListener() { 193 @Override 194 public void onClick(View v) { 195 sendAcknowledgement(mToken, false, mObserver); 196 mAllowButton.setEnabled(false); 197 mDenyButton.setEnabled(false); 198 } 199 }); 200 201 // if we're a relaunch we may need to adjust button enable state 202 if (icicle != null) { 203 mDidAcknowledge = icicle.getBoolean(DID_ACKNOWLEDGE, false); 204 mAllowButton.setEnabled(!mDidAcknowledge); 205 mDenyButton.setEnabled(!mDidAcknowledge); 206 } 207 } 208 209 // Preserve the restore observer callback binder across activity relaunch 210 @Override 211 public Object onRetainNonConfigurationInstance() { 212 return mObserver; 213 } 214 215 @Override 216 protected void onSaveInstanceState(Bundle outState) { 217 outState.putBoolean(DID_ACKNOWLEDGE, mDidAcknowledge); 218 } 219 220 void sendAcknowledgement(int token, boolean allow, IFullBackupRestoreObserver observer) { 221 if (!mDidAcknowledge) { 222 mDidAcknowledge = true; 223 224 try { 225 mBackupManager.acknowledgeFullBackupOrRestore(mToken, 226 allow, 227 String.valueOf(mCurPassword.getText()), 228 String.valueOf(mEncPassword.getText()), 229 mObserver); 230 } catch (RemoteException e) { 231 // TODO: bail gracefully if we can't contact the backup manager 232 } 233 } 234 } 235 236 boolean haveBackupPassword() { 237 try { 238 return mBackupManager.hasBackupPassword(); 239 } catch (RemoteException e) { 240 return true; // in the failure case, assume we need one 241 } 242 } 243 244 /** 245 * The observer binder for showing backup/restore progress. This binder just bounces 246 * the notifications onto the main thread. 247 */ 248 class FullObserver extends IFullBackupRestoreObserver.Stub { 249 private Handler mHandler; 250 251 public FullObserver(Handler h) { 252 mHandler = h; 253 } 254 255 public void setHandler(Handler h) { 256 mHandler = h; 257 } 258 259 // 260 // IFullBackupRestoreObserver implementation 261 // 262 @Override 263 public void onStartBackup() throws RemoteException { 264 mHandler.sendEmptyMessage(MSG_START_BACKUP); 265 } 266 267 @Override 268 public void onBackupPackage(String name) throws RemoteException { 269 mHandler.sendMessage(mHandler.obtainMessage(MSG_BACKUP_PACKAGE, name)); 270 } 271 272 @Override 273 public void onEndBackup() throws RemoteException { 274 mHandler.sendEmptyMessage(MSG_END_BACKUP); 275 } 276 277 @Override 278 public void onStartRestore() throws RemoteException { 279 mHandler.sendEmptyMessage(MSG_START_RESTORE); 280 } 281 282 @Override 283 public void onRestorePackage(String name) throws RemoteException { 284 mHandler.sendMessage(mHandler.obtainMessage(MSG_RESTORE_PACKAGE, name)); 285 } 286 287 @Override 288 public void onEndRestore() throws RemoteException { 289 mHandler.sendEmptyMessage(MSG_END_RESTORE); 290 } 291 292 @Override 293 public void onTimeout() throws RemoteException { 294 mHandler.sendEmptyMessage(MSG_TIMEOUT); 295 } 296 } 297} 298