diff --git a/src/com/android/settings/BackupSettingsActivity.java b/src/com/android/settings/BackupSettingsActivity.java index a4cc4b76032..c0456b2c795 100644 --- a/src/com/android/settings/BackupSettingsActivity.java +++ b/src/com/android/settings/BackupSettingsActivity.java @@ -17,19 +17,13 @@ package com.android.settings; import android.app.Activity; -import android.app.backup.BackupManager; -import android.app.backup.IBackupManager; -import android.content.Context; +import android.content.ComponentName; import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Bundle; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.text.TextUtils; import android.util.Log; -import com.android.settings.R; - -import java.net.URISyntaxException; +import com.android.settings.Settings.PrivacySettingsActivity; /** * A trampoline activity used to launch the configured Backup activity. @@ -42,30 +36,24 @@ public class BackupSettingsActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - String backup = getResources().getString(R.string.config_backup_settings_intent); - if (!TextUtils.isEmpty(backup)) { - try { - Intent intent = Intent.parseUri(backup, 0); - if (intent.resolveActivity(getPackageManager()) != null) { - // use startActivityForResult to let the activity check the caller signature - IBackupManager bmgr = IBackupManager.Stub.asInterface( - ServiceManager.getService(Context.BACKUP_SERVICE)); - boolean backupOkay; - try { - backupOkay = bmgr.isBackupServiceActive(UserHandle.myUserId()); - } catch (Exception e) { - // things go wrong talking to the backup system => ignore and - // pass the default 'false' as the "backup is a thing?" state. - backupOkay = false; - } - intent.putExtra(BackupManager.EXTRA_BACKUP_SERVICES_AVAILABLE, backupOkay); - startActivityForResult(intent, -1); - } else { - Log.e(TAG, "Backup component not found!"); - } - } catch (URISyntaxException e) { - Log.e(TAG, "Invalid backup component URI!", e); + + BackupSettingsHelper backupHelper = new BackupSettingsHelper(); + if (backupHelper.isIntentProvidedByTransport(getPackageManager())) { + Intent intent = backupHelper.getIntentForBackupSettings(); + if (intent != null) { + // use startActivityForResult to let the activity check the caller signature + startActivityForResult(intent, -1); } + } else { + // This should never happen, because isIntentProvidedByTransport() is called before + // starting this activity. + Log.e(TAG, "Backup transport has not provided an intent" + + " or the component for the intent is not found!"); + getPackageManager().setComponentEnabledSetting( + new ComponentName(getPackageName(), PrivacySettingsActivity.class.getName()), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP); + startActivityForResult(new Intent(this, PrivacySettingsActivity.class), -1); } finish(); } diff --git a/src/com/android/settings/BackupSettingsHelper.java b/src/com/android/settings/BackupSettingsHelper.java new file mode 100644 index 00000000000..37c0971b86d --- /dev/null +++ b/src/com/android/settings/BackupSettingsHelper.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settings; + + +import android.app.backup.BackupManager; +import android.app.backup.BackupTransport; +import android.app.backup.IBackupManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Helper class for {@link BackupSettingsActivity} that interacts with {@link IBackupManager}. + */ +public class BackupSettingsHelper { + private static final String TAG = "BackupSettingsHelper"; + + private IBackupManager mBackupManager = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + + + /** + * Gets the intent from Backup transport and adds the extra depending on whether the user has + * rights to see backup settings. + * + * @return Intent to launch Backup settings provided by the Backup transport. + */ + public Intent getIntentForBackupSettings() { + Intent intent = getIntentFromBackupTransport(); + if (intent != null) { + intent.putExtra(BackupManager.EXTRA_BACKUP_SERVICES_AVAILABLE, isBackupServiceActive()); + } + return intent; + } + + + /** + * Checks if the transport provided the intent to launch the backup settings and if that + * intent resolves to an activity. + */ + public boolean isIntentProvidedByTransport(PackageManager packageManager) { + Intent intent = getIntentFromBackupTransport(); + return intent != null && intent.resolveActivity(packageManager) != null; + } + + /** + * Gets an intent to launch the backup settings from the current transport using + * {@link BackupTransport#dataManagementIntent()} API. + * + * @return intent provided by transport or null if no intent was provided. + */ + private Intent getIntentFromBackupTransport() { + try { + Intent intent = + mBackupManager.getDataManagementIntent(mBackupManager.getCurrentTransport()); + if (Log.isLoggable(TAG, Log.DEBUG)) { + if (intent != null) { + Log.d(TAG, "Parsed intent from backup transport: " + intent.toString()); + } else { + Log.d(TAG, "Received a null intent from backup transport"); + } + } + return intent; + } catch (RemoteException e) { + Log.e(TAG, "Error getting data management intent", e); + } + return null; + } + + /** Checks if backup service is enabled for this user. */ + private boolean isBackupServiceActive() { + boolean backupOkay; + try { + backupOkay = mBackupManager.isBackupServiceActive(UserHandle.myUserId()); + } catch (Exception e) { + // things go wrong talking to the backup system => ignore and + // pass the default 'false' as the "backup is a thing?" state. + backupOkay = false; + } + return backupOkay; + } +} diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index 13bc0488d3b..7fe116762b3 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -33,6 +33,8 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.nfc.NfcAdapter; import android.os.AsyncTask; +import android.os.Build; +import android.os.Build.VERSION; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; @@ -969,29 +971,27 @@ public class SettingsActivity extends SettingsDrawerActivity } } - String backupIntent = getResources().getString(R.string.config_backup_settings_intent); - boolean useDefaultBackup = TextUtils.isEmpty(backupIntent); + // Check if the backup transport has provided an intent to launch the backup settings. + BackupSettingsHelper backupHelper = new BackupSettingsHelper(); + boolean useDefaultBackup = !backupHelper.isIntentProvidedByTransport(getPackageManager()); + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "Enabling default backup settings page: " + useDefaultBackup); + } + setTileEnabled(new ComponentName(packageName, Settings.PrivacySettingsActivity.class.getName()), useDefaultBackup, isAdmin); setTileEnabled(new ComponentName(packageName, "com.android.settings.PrivacyDashboardAlias"), useDefaultBackup, isAdmin); - boolean hasBackupActivity = false; - if (!useDefaultBackup) { - try { - Intent intent = Intent.parseUri(backupIntent, 0); - hasBackupActivity = !getPackageManager().queryIntentActivities(intent, 0).isEmpty(); - } catch (URISyntaxException e) { - Log.e(LOG_TAG, "Invalid backup intent URI!", e); - } - } - // Enable/disable BackupSettingsActivity and its alias. + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "Enabling transport provided backup settings: " + !useDefaultBackup); + } setTileEnabled(new ComponentName(packageName, - BackupSettingsActivity.class.getName()), hasBackupActivity, isAdmin); + BackupSettingsActivity.class.getName()), !useDefaultBackup, isAdmin); setTileEnabled(new ComponentName(packageName, - "com.android.settings.BackupResetDashboardAlias"), hasBackupActivity, isAdmin); + "com.android.settings.BackupResetDashboardAlias"), !useDefaultBackup, isAdmin); setTileEnabled(new ComponentName(packageName, Settings.EnterprisePrivacySettingsActivity.class.getName()), diff --git a/tests/robotests/src/com/android/settings/BackupSettingsHelperTest.java b/tests/robotests/src/com/android/settings/BackupSettingsHelperTest.java new file mode 100644 index 00000000000..b14debcfc05 --- /dev/null +++ b/tests/robotests/src/com/android/settings/BackupSettingsHelperTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settings; + +import android.app.backup.BackupManager; +import android.app.backup.IBackupManager; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.IBinder; +import android.os.RemoteException; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + + + + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, + shadows = {BackupSettingsHelperTest.ShadowBackupManagerStub.class}) +public class BackupSettingsHelperTest { + + private BackupSettingsHelper mBackupSettingsHelper; + + @Mock + private static IBackupManager mBackupManager; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mBackupManager.getCurrentTransport()).thenReturn("test_transport"); + mBackupSettingsHelper = new BackupSettingsHelper(); + } + + @Test + public void testGetIntentFromBackupTransport() throws Exception { + Intent intent = new Intent(); + + when(mBackupManager.getDataManagementIntent(anyString())).thenReturn(intent); + + Intent backupIntent = mBackupSettingsHelper.getIntentForBackupSettings(); + + verify(mBackupManager).getDataManagementIntent(anyString()); + } + + @Test + public void testGetIntentFromBackupTransport_WithIntent() throws Exception { + Intent intent = mock(Intent.class); + + when(mBackupManager.getDataManagementIntent(anyString())).thenReturn(intent); + + Intent backupIntent = mBackupSettingsHelper.getIntentForBackupSettings(); + + assertThat(backupIntent).isEqualTo(intent); + } + + @Test + public void testGetIntentFromBackupTransport_WithNullIntent() throws Exception { + when(mBackupManager.getDataManagementIntent(anyString())).thenReturn(null); + + Intent backupIntent = mBackupSettingsHelper.getIntentForBackupSettings(); + + assertThat(backupIntent).isNull(); + } + + @Test + public void testGetIntentFromBackupTransport_RemoteException() throws Exception { + when(mBackupManager.getDataManagementIntent(anyString())).thenThrow(new RemoteException()); + + Intent backupIntent = mBackupSettingsHelper.getIntentForBackupSettings(); + + assertThat(backupIntent).isNull(); + } + + @Test + public void testGetIntentFromBackupTransport_BackupEnabled() throws Exception { + Intent intent = new Intent("test_intent"); + + when(mBackupManager.getDataManagementIntent(anyString())).thenReturn(intent); + when(mBackupManager.isBackupServiceActive(anyInt())).thenReturn(true); + + Intent backupIntent = mBackupSettingsHelper.getIntentForBackupSettings(); + + assertThat(backupIntent.getExtras().get(BackupManager.EXTRA_BACKUP_SERVICES_AVAILABLE)) + .isEqualTo(true); + } + + @Test + public void testGetIntentFromBackupTransport_BackupDisabled() throws Exception { + Intent intent = new Intent("test_intent"); + + when(mBackupManager.getDataManagementIntent(anyString())).thenReturn(intent); + when(mBackupManager.isBackupServiceActive(anyInt())).thenReturn(false); + + Intent backupIntent = mBackupSettingsHelper.getIntentForBackupSettings(); + + assertThat(backupIntent.getExtras().get(BackupManager.EXTRA_BACKUP_SERVICES_AVAILABLE)) + .isEqualTo(false); + } + + @Test + public void testGetIntentFromBackupTransport_BackupStatusException() throws Exception { + Intent intent = new Intent("test_intent"); + + when(mBackupManager.getDataManagementIntent(anyString())).thenReturn(intent); + when(mBackupManager.isBackupServiceActive(anyInt())).thenThrow(new RemoteException()); + + Intent backupIntent = mBackupSettingsHelper.getIntentForBackupSettings(); + + assertThat(backupIntent.getExtras().get(BackupManager.EXTRA_BACKUP_SERVICES_AVAILABLE)) + .isEqualTo(false); + } + + @Test + public void testIsIntentProvidedByTransport_WithNullIntent() throws Exception { + when(mBackupManager.getDataManagementIntent(anyString())).thenReturn(null); + + PackageManager packageManager = mock(PackageManager.class); + + boolean isIntentProvided = mBackupSettingsHelper.isIntentProvidedByTransport(packageManager); + + assertThat(isIntentProvided).isFalse(); + } + + @Test + public void testIsIntentProvidedByTransport_WithInvalidIntent() throws Exception { + Intent intent = mock(Intent.class); + + when(mBackupManager.getDataManagementIntent(anyString())).thenReturn(intent); + + PackageManager packageManager = mock(PackageManager.class); + when(intent.resolveActivity(packageManager)).thenReturn(null); + + boolean isIntentProvided = mBackupSettingsHelper.isIntentProvidedByTransport(packageManager); + + assertThat(isIntentProvided).isFalse(); + } + + @Test + public void testIsIntentProvidedByTransport_WithIntent() throws Exception { + Intent intent = mock(Intent.class); + + when(mBackupManager.getDataManagementIntent(anyString())).thenReturn(intent); + + PackageManager packageManager = mock(PackageManager.class); + when(intent.resolveActivity(packageManager)).thenReturn(mock(ComponentName.class)); + + boolean isIntentProvided = mBackupSettingsHelper.isIntentProvidedByTransport(packageManager); + + assertThat(isIntentProvided).isTrue(); + } + + @Implements(IBackupManager.Stub.class) + public static class ShadowBackupManagerStub { + @Implementation + public static IBackupManager asInterface(IBinder iBinder) { + return mBackupManager; + } + } +}