AccountSyncSettingsBackupHelper.java revision 35f94d02ed59f311ab014e5686156a9d7bf1ea14
1a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul/* 2a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Copyright (C) 2014 The Android Open Source Project 3a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * 4a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Licensed under the Apache License, Version 2.0 (the "License"); 5a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * you may not use this file except in compliance with the License. 6a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * You may obtain a copy of the License at 7a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * 8a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * http://www.apache.org/licenses/LICENSE-2.0 9a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * 10a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Unless required by applicable law or agreed to in writing, software 11a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * distributed under the License is distributed on an "AS IS" BASIS, 12a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * See the License for the specific language governing permissions and 14a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * limitations under the License. 15a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul */ 16a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 17a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulpackage com.android.server.backup; 18a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 19a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport org.json.JSONArray; 20a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport org.json.JSONException; 21a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport org.json.JSONObject; 22a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 23a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.accounts.Account; 24a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.accounts.AccountManager; 25a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.app.backup.BackupDataInputStream; 26a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.app.backup.BackupDataOutput; 27a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.app.backup.BackupHelper; 28a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.content.ContentResolver; 29a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.content.Context; 30a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.content.SyncAdapterType; 31a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.os.ParcelFileDescriptor; 32a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.util.Log; 33a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 34a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.io.BufferedOutputStream; 35a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.io.DataInputStream; 36a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.io.DataOutputStream; 37a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.io.EOFException; 38a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.io.FileInputStream; 39a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.io.FileOutputStream; 40a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.io.IOException; 41a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.security.MessageDigest; 42a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.security.NoSuchAlgorithmException; 43a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.util.ArrayList; 44a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.util.Arrays; 45a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.util.HashMap; 46a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.util.HashSet; 47a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport java.util.List; 48a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 49a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul/** 50a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Helper for backing up account sync settings (whether or not a service should be synced). The 51a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * sync settings are backed up as a JSON object containing all the necessary information for 52a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * restoring the sync settings later. 53a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul */ 54a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulpublic class AccountSyncSettingsBackupHelper implements BackupHelper { 55a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 56a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String TAG = "AccountSyncSettingsBackupHelper"; 57a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final boolean DEBUG = false; 58a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 59a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final int STATE_VERSION = 1; 60a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final int MD5_BYTE_SIZE = 16; 61a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final int SYNC_REQUEST_LATCH_TIMEOUT_SECONDS = 1; 62a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 63a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String JSON_FORMAT_HEADER_KEY = "account_data"; 64a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String JSON_FORMAT_ENCODING = "UTF-8"; 65a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final int JSON_FORMAT_VERSION = 1; 66a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 67a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String KEY_VERSION = "version"; 68a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String KEY_MASTER_SYNC_ENABLED = "masterSyncEnabled"; 69a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String KEY_ACCOUNTS = "accounts"; 70a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String KEY_ACCOUNT_NAME = "name"; 71a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String KEY_ACCOUNT_TYPE = "type"; 72a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String KEY_ACCOUNT_AUTHORITIES = "authorities"; 73a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String KEY_AUTHORITY_NAME = "name"; 74a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String KEY_AUTHORITY_SYNC_STATE = "syncState"; 75a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private static final String KEY_AUTHORITY_SYNC_ENABLED = "syncEnabled"; 76a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 77a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private Context mContext; 78a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private AccountManager mAccountManager; 79a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 80a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul public AccountSyncSettingsBackupHelper(Context context) { 81a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul mContext = context; 82a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul mAccountManager = AccountManager.get(mContext); 83a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 84a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 85a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul /** 86a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Take a snapshot of the current account sync settings and write them to the given output. 87a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul */ 88a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul @Override 89a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput output, 90a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul ParcelFileDescriptor newState) { 91a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul try { 92a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONObject dataJSON = serializeAccountSyncSettingsToJSON(); 93a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 94a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul if (DEBUG) { 95a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul Log.d(TAG, "Account sync settings JSON: " + dataJSON); 96a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 97a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 98a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Encode JSON data to bytes. 99a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul byte[] dataBytes = dataJSON.toString().getBytes(JSON_FORMAT_ENCODING); 100a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul byte[] oldMd5Checksum = readOldMd5Checksum(oldState); 101a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul byte[] newMd5Checksum = generateMd5Checksum(dataBytes); 102a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul if (!Arrays.equals(oldMd5Checksum, newMd5Checksum)) { 103a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul int dataSize = dataBytes.length; 104a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul output.writeEntityHeader(JSON_FORMAT_HEADER_KEY, dataSize); 105a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul output.writeEntityData(dataBytes, dataSize); 106a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 107a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul Log.i(TAG, "Backup successful."); 108a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } else { 109a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul Log.i(TAG, "Old and new MD5 checksums match. Skipping backup."); 110a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 111a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 112a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul writeNewMd5Checksum(newState, newMd5Checksum); 113a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } catch (JSONException | IOException | NoSuchAlgorithmException e) { 114a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul Log.e(TAG, "Couldn't backup account sync settings\n" + e); 115a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 116a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 117a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 118a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul /** 119a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Fetch and serialize Account and authority information as a JSON Array. 120a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul */ 121a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private JSONObject serializeAccountSyncSettingsToJSON() throws JSONException { 122a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul Account[] accounts = mAccountManager.getAccounts(); 123a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser( 124a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul mContext.getUserId()); 125a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 126a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Create a map of Account types to authorities. Later this will make it easier for us to 127a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // generate our JSON. 128a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul HashMap<String, List<String>> accountTypeToAuthorities = new HashMap<String, 129a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul List<String>>(); 130a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul for (SyncAdapterType syncAdapter : syncAdapters) { 131a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Skip adapters that aren’t visible to the user. 132a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul if (!syncAdapter.isUserVisible()) { 133a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul continue; 134a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 135a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul if (!accountTypeToAuthorities.containsKey(syncAdapter.accountType)) { 136a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul accountTypeToAuthorities.put(syncAdapter.accountType, new ArrayList<String>()); 137a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 138a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul accountTypeToAuthorities.get(syncAdapter.accountType).add(syncAdapter.authority); 139a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 140a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 141a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Generate JSON. 142a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONObject backupJSON = new JSONObject(); 143a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul backupJSON.put(KEY_VERSION, JSON_FORMAT_VERSION); 144a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul backupJSON.put(KEY_MASTER_SYNC_ENABLED, ContentResolver.getMasterSyncAutomatically()); 145a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 146a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONArray accountJSONArray = new JSONArray(); 147a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul for (Account account : accounts) { 148a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul List<String> authorities = accountTypeToAuthorities.get(account.type); 149a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 150a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // We ignore Accounts that don't have any authorities because there would be no sync 151a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // settings for us to restore. 152a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul if (authorities == null || authorities.isEmpty()) { 153a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul continue; 154a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 155a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 156a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONObject accountJSON = new JSONObject(); 157a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul accountJSON.put(KEY_ACCOUNT_NAME, account.name); 158a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul accountJSON.put(KEY_ACCOUNT_TYPE, account.type); 159a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 160a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Add authorities for this Account type and check whether or not sync is enabled. 161a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONArray authoritiesJSONArray = new JSONArray(); 162a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul for (String authority : authorities) { 163a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul int syncState = ContentResolver.getIsSyncable(account, authority); 164a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority); 165a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 166a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONObject authorityJSON = new JSONObject(); 167a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul authorityJSON.put(KEY_AUTHORITY_NAME, authority); 168a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul authorityJSON.put(KEY_AUTHORITY_SYNC_STATE, syncState); 169a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul authorityJSON.put(KEY_AUTHORITY_SYNC_ENABLED, syncEnabled); 170a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul authoritiesJSONArray.put(authorityJSON); 171a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 172a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul accountJSON.put(KEY_ACCOUNT_AUTHORITIES, authoritiesJSONArray); 173a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 174a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul accountJSONArray.put(accountJSON); 175a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 176a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul backupJSON.put(KEY_ACCOUNTS, accountJSONArray); 177a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 178a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul return backupJSON; 179a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 180a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 181a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul /** 182a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Read the MD5 checksum from the old state. 183a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * 184a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * @return the old MD5 checksum 185a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul */ 186a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private byte[] readOldMd5Checksum(ParcelFileDescriptor oldState) throws IOException { 187a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul DataInputStream dataInput = new DataInputStream( 188a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul new FileInputStream(oldState.getFileDescriptor())); 189a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 190a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul byte[] oldMd5Checksum = new byte[MD5_BYTE_SIZE]; 191a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul try { 192a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul int stateVersion = dataInput.readInt(); 193a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul if (stateVersion <= STATE_VERSION) { 194a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // If the state version is a version we can understand then read the MD5 sum, 195a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // otherwise we return an empty byte array for the MD5 sum which will force a 196a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // backup. 197a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul for (int i = 0; i < MD5_BYTE_SIZE; i++) { 198a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul oldMd5Checksum[i] = dataInput.readByte(); 199a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 200a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } else { 201a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul Log.i(TAG, "Backup state version is: " + stateVersion 202a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul + " (support only up to version " + STATE_VERSION + ")"); 203a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 204a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } catch (EOFException eof) { 205a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Initial state may be empty. 206a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 20735f94d02ed59f311ab014e5686156a9d7bf1ea14Ritesh Reddy // We explicitly don't close 'dataInput' because we must not close the backing fd. 208a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul return oldMd5Checksum; 209a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 210a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 211a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul /** 212a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Write the given checksum to the file descriptor. 213a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul */ 214a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private void writeNewMd5Checksum(ParcelFileDescriptor newState, byte[] md5Checksum) 215a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul throws IOException { 216a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul DataOutputStream dataOutput = new DataOutputStream( 217a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul new BufferedOutputStream(new FileOutputStream(newState.getFileDescriptor()))); 218a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 219a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul dataOutput.writeInt(STATE_VERSION); 220a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul dataOutput.write(md5Checksum); 22135f94d02ed59f311ab014e5686156a9d7bf1ea14Ritesh Reddy 22235f94d02ed59f311ab014e5686156a9d7bf1ea14Ritesh Reddy // We explicitly don't close 'dataOutput' because we must not close the backing fd. 22335f94d02ed59f311ab014e5686156a9d7bf1ea14Ritesh Reddy // The FileOutputStream will not close it implicitly. 22435f94d02ed59f311ab014e5686156a9d7bf1ea14Ritesh Reddy 225a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 226a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 227a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private byte[] generateMd5Checksum(byte[] data) throws NoSuchAlgorithmException { 228a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul if (data == null) { 229a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul return null; 230a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 231a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 232a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul MessageDigest md5 = MessageDigest.getInstance("MD5"); 233a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul return md5.digest(data); 234a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 235a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 236a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul /** 237a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Restore account sync settings from the given data input stream. 238a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul */ 239a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul @Override 240a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul public void restoreEntity(BackupDataInputStream data) { 241a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul byte[] dataBytes = new byte[data.size()]; 242a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul try { 243a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Read the data and convert it to a String. 244a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul data.read(dataBytes); 245a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul String dataString = new String(dataBytes, JSON_FORMAT_ENCODING); 246a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 247a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Convert data to a JSON object. 248a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONObject dataJSON = new JSONObject(dataString); 249a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul boolean masterSyncEnabled = dataJSON.getBoolean(KEY_MASTER_SYNC_ENABLED); 250a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONArray accountJSONArray = dataJSON.getJSONArray(KEY_ACCOUNTS); 251a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 252a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul boolean currentMasterSyncEnabled = ContentResolver.getMasterSyncAutomatically(); 253a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul if (currentMasterSyncEnabled) { 254a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Disable master sync to prevent any syncs from running. 255a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul ContentResolver.setMasterSyncAutomatically(false); 256a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 257a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 258a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul try { 259a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul HashSet<Account> currentAccounts = getAccountsHashSet(); 260a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul for (int i = 0; i < accountJSONArray.length(); i++) { 261a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONObject accountJSON = (JSONObject) accountJSONArray.get(i); 262a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul String accountName = accountJSON.getString(KEY_ACCOUNT_NAME); 263a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE); 264a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 265a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul Account account = new Account(accountName, accountType); 266a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 267a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Check if the account already exists. Accounts that don't exist on the device 268a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // yet won't be restored. 269a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul if (currentAccounts.contains(account)) { 270a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul restoreExistingAccountSyncSettingsFromJSON(accountJSON); 27153abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams } else { 27253abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams // TODO: 27353abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams // Stash the data to a file that the SyncManager can read from to restore 27453abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams // settings at a later date. 275a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 276a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 277a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } finally { 278a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Set the master sync preference to the value from the backup set. 279a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul ContentResolver.setMasterSyncAutomatically(masterSyncEnabled); 280a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 281a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 282a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul Log.i(TAG, "Restore successful."); 283a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } catch (IOException | JSONException e) { 284a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul Log.e(TAG, "Couldn't restore account sync settings\n" + e); 285a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 286a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 287a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 288a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul /** 289a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Helper method - fetch accounts and return them as a HashSet. 290a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * 291a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * @return Accounts in a HashSet. 292a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul */ 293a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private HashSet<Account> getAccountsHashSet() { 294a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul Account[] accounts = mAccountManager.getAccounts(); 295a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul HashSet<Account> accountHashSet = new HashSet<Account>(); 296a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul for (Account account : accounts) { 297a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul accountHashSet.add(account); 298a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 299a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul return accountHashSet; 300a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 301a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 302a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul /** 303a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * Restore account sync settings using the given JSON. This function won't work if the account 304a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul * doesn't exist yet. 30553abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * This function will only be called during Setup Wizard, where we are guaranteed that there 30653abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * are no active syncs. 30753abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * There are 2 pieces of data to restore - 30853abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * isSyncable (corresponds to {@link ContentResolver#getIsSyncable(Account, String)} 30953abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * syncEnabled (corresponds to {@link ContentResolver#getSyncAutomatically(Account, String)} 31053abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * <strong>The restore favours adapters that were enabled on the old device, and doesn't care 31153abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * about adapters that were disabled.</strong> 31253abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * 31353abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * syncEnabled=true in restore data. 31453abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * syncEnabled will be true on this device. isSyncable will be left as the default in order to 31553abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * give the enabled adapter the chance to run an initialization sync. 31653abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * 31753abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * syncEnabled=false in restore data. 31853abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * syncEnabled will be false on this device. isSyncable will be set to 2, unless it was 0 on the 31953abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * old device in which case it will be set to 0 on this device. This is because isSyncable=0 is 32053abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * a rare state and was probably set to 0 for good reason (historically isSyncable is a way by 32153abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * which adapters control their own sync state independently of sync settings which is 32253abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * toggleable by the user). 32353abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * isSyncable=2 is a new isSyncable state we introduced specifically to allow adapters that are 32453abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * disabled after a restore to run initialization logic when the adapter is later enabled. 32553abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * See com.android.server.content.SyncStorageEngine#setSyncAutomatically 32653abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * 32753abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * The end result is that an adapter that the user had on will be turned on and get an 32853abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * initialization sync, while an adapter that the user had off will be off until the user 32953abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams * enables it on this device at which point it will get an initialization sync. 330a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul */ 331a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul private void restoreExistingAccountSyncSettingsFromJSON(JSONObject accountJSON) 332a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul throws JSONException { 333a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul // Restore authorities. 334a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONArray authorities = accountJSON.getJSONArray(KEY_ACCOUNT_AUTHORITIES); 335a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul String accountName = accountJSON.getString(KEY_ACCOUNT_NAME); 336a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE); 33753abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams 338a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul final Account account = new Account(accountName, accountType); 339a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul for (int i = 0; i < authorities.length(); i++) { 340a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul JSONObject authority = (JSONObject) authorities.get(i); 341a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul final String authorityName = authority.getString(KEY_AUTHORITY_NAME); 34253abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams boolean wasSyncEnabled = authority.getBoolean(KEY_AUTHORITY_SYNC_ENABLED); 34353abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams int wasSyncable = authority.getInt(KEY_AUTHORITY_SYNC_STATE); 34453abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams 34553abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams ContentResolver.setSyncAutomaticallyAsUser( 34653abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams account, authorityName, wasSyncEnabled, 0 /* user Id */); 34753abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams 34853abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams if (!wasSyncEnabled) { 34953abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams ContentResolver.setIsSyncable( 35053abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams account, 35153abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams authorityName, 35253abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams wasSyncable == 0 ? 35353abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams 0 /* not syncable */ : 2 /* syncable but needs initialization */); 354a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 355a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 356a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 357a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 358a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul @Override 359a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul public void writeNewStateDescription(ParcelFileDescriptor newState) { 360a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul 361a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul } 362a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul} 363