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