Merge "Add developer option to opt-in updatable graphics driver."
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e7b04c3..e86917c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -678,7 +678,7 @@
     <!-- check box to allow data usage when roaming [CHAR LIMIT=41] -->
     <string name="allow_data_usage_title">Allow data usage when roaming</string>
     <!-- mobile network settings screen, setting check box title -->
-    <string name="roaming">Data roaming</string>
+    <string name="roaming">Roaming</string>
     <!-- mobile network settings screen, setting option summary text when check box is selected -->
     <string name="roaming_enable">Connect to data services when roaming</string>
     <!-- mobile network settings screen, setting option summary text when check box is clear -->
@@ -10250,6 +10250,8 @@
     <string name="mobile_data_usage_title">App data usage</string>
     <!-- Summary to show the current network mode is invalid. [CHAR LIMIT=NONE]-->
     <string name="mobile_network_mode_error">Invalid Network Mode <xliff:g id="networkModeId" example="0">%1$d</xliff:g>. Ignore.</string>
+    <!-- Title for Apn settings in mobile network settings [CHAR LIMIT=60] -->
+    <string name="mobile_network_apn_title">Access Point Names</string>
 
     <!-- Available networks screen, summary when button disallowed due to permanent automatic mode [CHAR LIMIT=NONE] -->
     <string name="manual_mode_disallowed_summary">Unavailable when connected to <xliff:g id="carrier" example="verizon">%1$s</xliff:g></string>
diff --git a/res/xml/gsm_umts_options.xml b/res/xml/gsm_umts_options.xml
deleted file mode 100644
index cf56595..0000000
--- a/res/xml/gsm_umts_options.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2008 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.
--->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-    <!--We want separate APN setting from reset of settings because-->
-    <!--we want user to change it with caution.-->
-    <PreferenceCategory
-        android:key="category_gsm_apn_key"
-        android:layout="@layout/preference_category_no_label">
-
-        <com.android.settingslib.RestrictedPreference
-            android:key="button_gsm_apn_key"
-            android:title="@string/apn_settings"
-            android:persistent="false" />
-    </PreferenceCategory>
-
-    <PreferenceScreen
-        android:key="carrier_settings_key"
-        android:title="@string/carrier_settings_title">
-        <!-- b/114749736, create a preference controller to build intent -->
-    </PreferenceScreen>
-
-</PreferenceScreen>
diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml
index 0478664..cc914ad 100644
--- a/res/xml/mobile_network_settings.xml
+++ b/res/xml/mobile_network_settings.xml
@@ -138,7 +138,7 @@
     <com.android.settingslib.RestrictedPreference
         android:key="telephony_apn_key"
         android:persistent="false"
-        android:title="@string/apn_settings"
+        android:title="@string/mobile_network_apn_title"
         settings:allowDividerAbove="true"
         settings:controller="com.android.settings.network.telephony.ApnPreferenceController"/>
 
diff --git a/src/com/android/settings/accounts/AccountFeatureProvider.java b/src/com/android/settings/accounts/AccountFeatureProvider.java
index 9829ca6..fd65095 100644
--- a/src/com/android/settings/accounts/AccountFeatureProvider.java
+++ b/src/com/android/settings/accounts/AccountFeatureProvider.java
@@ -18,8 +18,10 @@
 
 import android.accounts.Account;
 import android.content.Context;
+import android.content.Intent;
 
 public interface AccountFeatureProvider {
     String getAccountType();
     Account[] getAccounts(Context context);
+    Intent getAccountSettingsDeeplinkIntent();
 }
diff --git a/src/com/android/settings/accounts/AccountFeatureProviderImpl.java b/src/com/android/settings/accounts/AccountFeatureProviderImpl.java
index 90b581b..2e0f432 100644
--- a/src/com/android/settings/accounts/AccountFeatureProviderImpl.java
+++ b/src/com/android/settings/accounts/AccountFeatureProviderImpl.java
@@ -2,6 +2,7 @@
 
 import android.accounts.Account;
 import android.content.Context;
+import android.content.Intent;
 
 public class AccountFeatureProviderImpl implements AccountFeatureProvider {
     @Override
@@ -13,4 +14,9 @@
     public Account[] getAccounts(Context context) {
         return new Account[0];
     }
+
+    @Override
+    public Intent getAccountSettingsDeeplinkIntent() {
+        return null;
+    }
 }
diff --git a/src/com/android/settings/accounts/AvatarViewMixin.java b/src/com/android/settings/accounts/AvatarViewMixin.java
index 6dcf312..78750b1 100644
--- a/src/com/android/settings/accounts/AvatarViewMixin.java
+++ b/src/com/android/settings/accounts/AvatarViewMixin.java
@@ -17,18 +17,30 @@
 package com.android.settings.accounts;
 
 import android.accounts.Account;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
 import android.util.Log;
 import android.widget.ImageView;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.OnLifecycleEvent;
 
 import com.android.settings.R;
 import com.android.settings.homepage.SettingsHomepageActivity;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.List;
 
 /**
  * Avatar related work to the onStart method of registered observable classes
@@ -37,12 +49,39 @@
 public class AvatarViewMixin implements LifecycleObserver {
     private static final String TAG = "AvatarViewMixin";
 
+    @VisibleForTesting
+    static final Intent INTENT_GET_ACCOUNT_DATA =
+            new Intent("android.content.action.SETTINGS_ACCOUNT_DATA");
+
+    private static final String METHOD_GET_ACCOUNT_AVATAR = "getAccountAvatar";
+    private static final String KEY_AVATAR_BITMAP = "account_avatar";
+    private static final int REQUEST_CODE = 1013;
+
     private final Context mContext;
     private final ImageView mAvatarView;
+    private final MutableLiveData<Bitmap> mAvatarImage;
 
-    public AvatarViewMixin(Context context, ImageView avatarView) {
-        mContext = context.getApplicationContext();
+    public AvatarViewMixin(SettingsHomepageActivity activity, ImageView avatarView) {
+        mContext = activity.getApplicationContext();
         mAvatarView = avatarView;
+        mAvatarView.setOnClickListener(v -> {
+            if (hasAccount()) {
+                //TODO(b/117509285) launch the new page of the MeCard
+            } else {
+                final Intent intent = FeatureFactory.getFactory(mContext)
+                        .getAccountFeatureProvider()
+                        .getAccountSettingsDeeplinkIntent();
+
+                if (intent != null) {
+                    activity.startActivityForResult(intent, REQUEST_CODE);
+                }
+            }
+        });
+
+        mAvatarImage = new MutableLiveData<>();
+        mAvatarImage.observe(activity, bitmap -> {
+            avatarView.setImageBitmap(bitmap);
+        });
     }
 
     @OnLifecycleEvent(Lifecycle.Event.ON_START)
@@ -52,7 +91,7 @@
             return;
         }
         if (hasAccount()) {
-            //TODO(b/117509285): To migrate account icon on search bar
+            loadAvatar();
         } else {
             mAvatarView.setImageResource(R.drawable.ic_account_circle_24dp);
         }
@@ -64,4 +103,34 @@
                 mContext).getAccountFeatureProvider().getAccounts(mContext);
         return (accounts != null) && (accounts.length > 0);
     }
+
+    private void loadAvatar() {
+        final String authority = queryProviderAuthority();
+        if (TextUtils.isEmpty(authority)) {
+            return;
+        }
+
+        ThreadUtils.postOnBackgroundThread(() -> {
+            final Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                    .authority(authority)
+                    .build();
+            final Bundle bundle = mContext.getContentResolver().call(uri,
+                    METHOD_GET_ACCOUNT_AVATAR, null /* arg */, null /* extras */);
+            final Bitmap bitmap = bundle.getParcelable(KEY_AVATAR_BITMAP);
+            mAvatarImage.postValue(bitmap);
+        });
+    }
+
+    @VisibleForTesting
+    String queryProviderAuthority() {
+        final List<ResolveInfo> providers =
+                mContext.getPackageManager().queryIntentContentProviders(INTENT_GET_ACCOUNT_DATA,
+                        PackageManager.MATCH_SYSTEM_ONLY);
+        if (providers.size() == 1) {
+            return providers.get(0).providerInfo.authority;
+        } else {
+            Log.w(TAG, "The size of the provider is " + providers.size());
+            return null;
+        }
+    }
 }
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index 7f04bde..14a918e 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -264,6 +264,10 @@
             return false;
         }
 
+        protected Class<? extends ChooseLockGeneric.InternalActivity> getInternalActivityClass() {
+            return ChooseLockGeneric.InternalActivity.class;
+        }
+
         protected void addHeaderView() {
             if (mForFingerprint) {
                 setHeaderView(R.layout.choose_lock_generic_fingerprint_header);
@@ -291,7 +295,7 @@
                 return true;
             } else if (KEY_SKIP_FINGERPRINT.equals(key) || KEY_SKIP_FACE.equals(key)) {
                 Intent chooseLockGenericIntent = new Intent(getActivity(),
-                    ChooseLockGeneric.InternalActivity.class);
+                    getInternalActivityClass());
                 chooseLockGenericIntent.setAction(getIntent().getAction());
                 // Forward the target user id to  ChooseLockGeneric.
                 chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
diff --git a/src/com/android/settings/password/SetupChooseLockGeneric.java b/src/com/android/settings/password/SetupChooseLockGeneric.java
index 700cc2d..7600104 100644
--- a/src/com/android/settings/password/SetupChooseLockGeneric.java
+++ b/src/com/android/settings/password/SetupChooseLockGeneric.java
@@ -27,6 +27,7 @@
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import androidx.fragment.app.Fragment;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceFragmentCompat;
 import androidx.recyclerview.widget.RecyclerView;
@@ -135,6 +136,11 @@
             return true;
         }
 
+        @Override
+        protected Class<? extends ChooseLockGeneric.InternalActivity> getInternalActivityClass() {
+            return SetupChooseLockGeneric.InternalActivity.class;
+        }
+
         /***
          * Disables preferences that are less secure than required quality and shows only secure
          * screen lock options here.
@@ -207,4 +213,25 @@
             return intent;
         }
     }
+
+    public static class InternalActivity extends ChooseLockGeneric.InternalActivity {
+        @Override
+        protected boolean isValidFragment(String fragmentName) {
+            return InternalSetupChooseLockGenericFragment.class.getName().equals(fragmentName);
+        }
+
+        @Override
+        /* package */ Class<? extends Fragment> getFragmentClass() {
+            return InternalSetupChooseLockGenericFragment.class;
+        }
+
+        public static class InternalSetupChooseLockGenericFragment
+                extends ChooseLockGenericFragment {
+            @Override
+            protected boolean canRunBeforeDeviceProvisioned() {
+                return true;
+            }
+        }
+    }
+
 }
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index f9301a2..479ffee 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -58,6 +58,7 @@
 com.android.settings.notification.ZenModeEventRuleSettings
 com.android.settings.notification.ZenModeScheduleRuleSettings
 com.android.settings.password.ChooseLockGeneric$ChooseLockGenericFragment
+com.android.settings.password.SetupChooseLockGeneric$InternalActivity$InternalSetupChooseLockGenericFragment
 com.android.settings.password.SetupChooseLockGeneric$SetupChooseLockGenericFragment
 com.android.settings.print.PrintJobSettingsFragment
 com.android.settings.print.PrintServiceSettingsFragment
diff --git a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
index b3d929c..039d2e2 100644
--- a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
@@ -24,6 +24,10 @@
 
 import android.accounts.Account;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
 import android.widget.ImageView;
 
 import com.android.settings.homepage.SettingsHomepageActivity;
@@ -39,38 +43,45 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowPackageManager;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 public class AvatarViewMixinTest {
     private static final String DUMMY_ACCOUNT = "test@domain.com";
     private static final String DUMMY_DOMAIN = "domain.com";
+    private static final String DUMMY_AUTHORITY = "authority.domain.com";
 
     private Context mContext;
     private ImageView mImageView;
+    private ActivityController mController;
+    private SettingsHomepageActivity mActivity;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
         mImageView = new ImageView(mContext);
+        mController = Robolectric.buildActivity(SettingsHomepageActivity.class).create();
+        mActivity = (SettingsHomepageActivity) mController.get();
     }
 
     @Test
     public void hasAccount_useDefaultAccountData_returnFalse() {
-        final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mContext, mImageView);
+        final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mActivity, mImageView);
         assertThat(avatarViewMixin.hasAccount()).isFalse();
     }
 
     @Test
     @Config(shadows = ShadowAccountFeatureProviderImpl.class)
     public void hasAccount_useShadowAccountData_returnTrue() {
-        final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mContext, mImageView);
+        final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mActivity, mImageView);
         assertThat(avatarViewMixin.hasAccount()).isTrue();
     }
 
     @Test
     public void onStart_configDisabled_doNothing() {
-        final AvatarViewMixin mixin = spy(new AvatarViewMixin(mContext, mImageView));
+        final AvatarViewMixin mixin = spy(new AvatarViewMixin(mActivity, mImageView));
         mixin.onStart();
 
         verify(mixin, never()).hasAccount();
@@ -79,19 +90,45 @@
     @Test
     @Config(qualifiers = "mcc999")
     public void onStart_useMockAvatarViewMixin_shouldBeExecuted() {
-        final AvatarViewMixin mockAvatar = spy(new AvatarViewMixin(mContext, mImageView));
+        final AvatarViewMixin mockAvatar = spy(new AvatarViewMixin(mActivity, mImageView));
 
-        final ActivityController controller = Robolectric.buildActivity(
-                SettingsHomepageActivity.class).create();
-        final SettingsHomepageActivity settingsHomepageActivity =
-                (SettingsHomepageActivity) controller.get();
-        settingsHomepageActivity.getLifecycle().addObserver(mockAvatar);
-        controller.start();
+        mActivity.getLifecycle().addObserver(mockAvatar);
+        mController.start();
 
         verify(mockAvatar).hasAccount();
     }
 
-    @Implements(AccountFeatureProviderImpl.class)
+    @Test
+    public void queryProviderAuthority_useShadowPackagteManager_returnNull() {
+        final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mActivity, mImageView);
+
+        assertThat(avatarViewMixin.queryProviderAuthority()).isNull();
+    }
+
+    @Test
+    public void queryProviderAuthority_useNewShadowPackagteManager_returnAuthority() {
+        final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mActivity, mImageView);
+        ShadowPackageManager shadowPackageManager = Shadow.extract(mContext.getPackageManager());
+        final PackageInfo accountProvider = new PackageInfo();
+        accountProvider.packageName = "test.pkg";
+        accountProvider.applicationInfo = new ApplicationInfo();
+        accountProvider.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+        accountProvider.applicationInfo.packageName = accountProvider.packageName;
+        accountProvider.providers = new ProviderInfo[1];
+        accountProvider.providers[0] = new ProviderInfo();
+        accountProvider.providers[0].authority = DUMMY_AUTHORITY;
+        accountProvider.providers[0].packageName = accountProvider.packageName;
+        accountProvider.providers[0].name = "test.class";
+        accountProvider.providers[0].applicationInfo = accountProvider.applicationInfo;
+
+        final ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.providerInfo = accountProvider.providers[0];
+        shadowPackageManager.addResolveInfoForIntent(AvatarViewMixin.INTENT_GET_ACCOUNT_DATA,
+                resolveInfo);
+        assertThat(avatarViewMixin.queryProviderAuthority()).isEqualTo(DUMMY_AUTHORITY);
+    }
+
+    @Implements(value = AccountFeatureProviderImpl.class)
     public static class ShadowAccountFeatureProviderImpl {
 
         @Implementation
diff --git a/tests/unit/src/com/android/settings/password/SetupChooseLockGenericTest.java b/tests/unit/src/com/android/settings/password/SetupChooseLockGenericTest.java
new file mode 100644
index 0000000..ce3d08f
--- /dev/null
+++ b/tests/unit/src/com/android/settings/password/SetupChooseLockGenericTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.password;
+
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.test.InstrumentationRegistry.getTargetContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
+import android.support.test.runner.lifecycle.Stage;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiSelector;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collection;
+
+/**
+ * Tests for {@link SetupChooseLockGenericTest}
+ *
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SetupChooseLockGenericTest {
+
+    private UiDevice mDevice;
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mContext = getInstrumentation().getTargetContext();
+        Settings.Global.putInt(
+            mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
+    }
+
+    @After
+    public void tearDown() {
+        Settings.Global.putInt(
+            mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
+    }
+
+    @Test
+    public void clickSkipFigerprintPreference_deviceNotProvisioned_shouldBeAbleToProceed()
+            throws Throwable {
+        final Intent newPasswordIntent =
+            new Intent(getTargetContext(), SetupChooseLockGeneric.class)
+            .putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true)
+            .setAction(ACTION_SET_NEW_PASSWORD)
+            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+        getInstrumentation().getContext().startActivity(newPasswordIntent);
+        mDevice.waitForIdle();
+        mDevice.findObject(new UiSelector().textContains("Continue without ")).click();
+
+        final Activity activity = getCurrentActivity();
+        assertThat(activity).isInstanceOf(SetupChooseLockGeneric.InternalActivity.class);
+    }
+
+    private Activity getCurrentActivity() throws Throwable {
+        getInstrumentation().waitForIdleSync();
+        final Activity[] activity = new Activity[1];
+        getInstrumentation().runOnMainSync(() -> {
+            Collection<Activity> activities = ActivityLifecycleMonitorRegistry.getInstance()
+                    .getActivitiesInStage(Stage.RESUMED);
+            activity[0] = activities.iterator().next();
+        });
+        return activity[0];
+    }
+
+}