Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9cd03e1e authored by Brandon Maxwell's avatar Brandon Maxwell
Browse files

Fixes for FBE behavior changes

+ This change ensures that the Dialer doesn't try to use the
ContactsPreferences object (which uses information in unavailable
storage) while in File based encryption locked mode. Without these
checks, the Dialer crashes on receiving an incoming call while FBE
locked.
+ Added Factory method to create ContactsPreferences for tests/while
FBE locked/while FBE unlocked
+ Added tests for CallCardPresenter and StatusBarNotifier FBE related
code

Bug=26822105

Change-Id: I5df93526e70b7350885c7261982945c32b7e86a0
parent f68dac28
Loading
Loading
Loading
Loading
+24 −13
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.telecom.Call.Details;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccount;
@@ -42,6 +43,7 @@ import android.widget.ListAdapter;
import com.android.contacts.common.ContactsUtils;
import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.common.testing.NeededForTesting;
import com.android.contacts.common.util.ContactDisplayUtils;
import com.android.incallui.Call.State;
import com.android.incallui.ContactInfoCache.ContactCacheEntry;
@@ -83,7 +85,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
    private ContactCacheEntry mSecondaryContactInfo;
    private CallTimer mCallTimer;
    private Context mContext;
    private ContactsPreferences mContactsPreferences;
    @Nullable private ContactsPreferences mContactsPreferences;
    private boolean mSpinnerShowing = false;
    private boolean mHasShownToast = false;
    private InCallContactInteractions mInCallContactInteractions;
@@ -136,7 +138,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
    public void init(Context context, Call call) {
        mContext = Preconditions.checkNotNull(context);
        mDistanceHelper = ObjectFactory.newDistanceHelper(mContext, this);
        mContactsPreferences = new ContactsPreferences(mContext);
        mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);

        // Call may be null if disconnect happened already.
        if (call != null) {
@@ -932,11 +934,15 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
    /**
     * Gets the name to display for the call.
     */
    private String getNameForCall(ContactCacheEntry contactInfo) {
        String preferredName = ContactDisplayUtils.getPreferredDisplayName(
    @NeededForTesting
    String getNameForCall(ContactCacheEntry contactInfo) {
        String preferredName = contactInfo.namePrimary;
        if (mContactsPreferences != null) {
            preferredName = ContactDisplayUtils.getPreferredDisplayName(
                    contactInfo.namePrimary,
                    contactInfo.nameAlternative,
                    mContactsPreferences.getDisplayOrder());
        }
        if (TextUtils.isEmpty(preferredName)) {
            return contactInfo.number;
        }
@@ -946,13 +952,18 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
    /**
     * Gets the number to display for a call.
     */
    private String getNumberForCall(ContactCacheEntry contactInfo) {
        // If the name is empty, we use the number for the name...so dont show a second
    @NeededForTesting
    String getNumberForCall(ContactCacheEntry contactInfo) {
        // If the name is empty, we use the number for the name...so don't show a second
        // number in the number field
        if (TextUtils.isEmpty(ContactDisplayUtils.getPreferredDisplayName(
        String preferredName = contactInfo.namePrimary;
        if (mContactsPreferences != null) {
            preferredName = ContactDisplayUtils.getPreferredDisplayName(
                    contactInfo.namePrimary,
                    contactInfo.nameAlternative,
                mContactsPreferences.getDisplayOrder()))) {
                    mContactsPreferences.getDisplayOrder());
        }
        if (TextUtils.isEmpty(preferredName)) {
            return contactInfo.location;
        }
        return contactInfo.number;
+61 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.incallui;

import android.content.Context;
import android.support.annotation.Nullable;
import com.android.dialer.compat.UserManagerCompat;

import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.common.testing.NeededForTesting;

/**
 * Factory class for {@link ContactsPreferences}.
 */
public class ContactsPreferencesFactory {

    private static boolean sUseTestInstance;
    private static ContactsPreferences sTestInstance;

    /**
     * Creates a new {@link ContactsPreferences} object if possible.
     *
     * @param context the context to use when creating the ContactsPreferences.
     * @return a new ContactsPreferences object or {@code null} if the user is locked.
     */
    @Nullable
    public static ContactsPreferences newContactsPreferences(Context context) {
        if (sUseTestInstance) {
            return sTestInstance;
        }
        if (UserManagerCompat.isUserUnlocked(context)) {
            return new ContactsPreferences(context);
        }
        return null;
    }

    /**
     * Sets the instance to be returned by all calls to {@link #newContactsPreferences(Context)}.
     *
     * @param testInstance the instance to return.
     */
    @NeededForTesting
    static void setTestInstance(ContactsPreferences testInstance) {
        sUseTestInstance = true;
        sTestInstance = testInstance;
    }
}
+11 −6
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.media.AudioAttributes;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.telecom.Call.Details;
import android.telecom.PhoneAccount;
import android.telecom.TelecomManager;
@@ -46,6 +47,7 @@ import android.text.TextUtils;
import com.android.contacts.common.ContactsUtils;
import com.android.contacts.common.ContactsUtils.UserType;
import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.common.testing.NeededForTesting;
import com.android.contacts.common.util.BitmapUtil;
import com.android.contacts.common.util.ContactDisplayUtils;
import com.android.incallui.ContactInfoCache.ContactCacheEntry;
@@ -72,7 +74,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
    private static final long[] VIBRATE_PATTERN = new long[] {0, 1000, 1000};

    private final Context mContext;
    private final ContactsPreferences mContactsPreferences;
    @Nullable private ContactsPreferences mContactsPreferences;
    private final ContactInfoCache mContactInfoCache;
    private final NotificationManager mNotificationManager;
    private final DialerRingtoneManager mDialerRingtoneManager;
@@ -89,7 +91,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
    public StatusBarNotifier(Context context, ContactInfoCache contactInfoCache) {
        Preconditions.checkNotNull(context);
        mContext = context;
        mContactsPreferences = new ContactsPreferences(context);
        mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);
        mContactInfoCache = contactInfoCache;
        mNotificationManager =
                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -389,18 +391,21 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
    /**
     * Returns the main string to use in the notification.
     */
    private String getContentTitle(ContactCacheEntry contactInfo, Call call) {
    @NeededForTesting
    String getContentTitle(ContactCacheEntry contactInfo, Call call) {
        if (call.isConferenceCall() && !call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)) {
            return mContext.getResources().getString(R.string.card_title_conf_call);
        }

        String preferredName = ContactDisplayUtils.getPreferredDisplayName(contactInfo.namePrimary,
        String preferredName = contactInfo.namePrimary;
        if (mContactsPreferences != null) {
            preferredName = ContactDisplayUtils.getPreferredDisplayName(contactInfo.namePrimary,
                    contactInfo.nameAlternative, mContactsPreferences.getDisplayOrder());
        }
        if (TextUtils.isEmpty(preferredName)) {
            return TextUtils.isEmpty(contactInfo.number) ? null : BidiFormatter.getInstance()
                    .unicodeWrap(contactInfo.number, TextDirectionHeuristics.LTR);
        }

        return preferredName;
    }

+121 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.incallui;

import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;

import com.android.contacts.common.preference.ContactsPreferences;
import com.android.incallui.ContactInfoCache.ContactCacheEntry;

import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

@MediumTest
public class CallCardPresenterTest extends AndroidTestCase {

    private static final String NAME_PRIMARY = "Full Name";
    private static final String NAME_ALTERNATIVE = "Name, Full";
    private static final String LOCATION = "US";
    private static final String NUMBER = "8006459001";

    @Mock private ContactsPreferences mContactsPreferences;
    private ContactCacheEntry mUnlockedContactInfo;
    private ContactCacheEntry mLockedContactInfo;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        MockitoAnnotations.initMocks(this);

        Mockito.when(mContactsPreferences.getDisplayOrder())
                .thenReturn(ContactsPreferences.DISPLAY_ORDER_PRIMARY);

        // Unlocked all contact info is available
        mUnlockedContactInfo = new ContactCacheEntry();
        mUnlockedContactInfo.namePrimary = NAME_PRIMARY;
        mUnlockedContactInfo.nameAlternative = NAME_ALTERNATIVE;
        mUnlockedContactInfo.location = LOCATION;
        mUnlockedContactInfo.number = NUMBER;

        // Locked only number and location are available
        mLockedContactInfo = new ContactCacheEntry();
        mLockedContactInfo .location = LOCATION;
        mLockedContactInfo .number = NUMBER;
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
        ContactsPreferencesFactory.setTestInstance(null);
    }

    public void testGetNameForCall_Unlocked() {
        ContactsPreferencesFactory.setTestInstance(mContactsPreferences);
        CallCardPresenter presenter = new CallCardPresenter();
        presenter.init(getContext(), null);

        assertEquals(NAME_PRIMARY, presenter.getNameForCall(mUnlockedContactInfo));
    }

    public void testGetNameForCall_Locked() {
        ContactsPreferencesFactory.setTestInstance(null);
        CallCardPresenter presenter = new CallCardPresenter();
        presenter.init(getContext(), null);

        assertEquals(NUMBER, presenter.getNameForCall(mLockedContactInfo));
    }

    public void testGetNameForCall_EmptyPreferredName() {
        ContactCacheEntry contactInfo = new ContactCacheEntry();
        contactInfo.number = NUMBER;

        ContactsPreferencesFactory.setTestInstance(null);
        CallCardPresenter presenter = new CallCardPresenter();
        presenter.init(getContext(), null);

        assertEquals(NUMBER, presenter.getNameForCall(contactInfo));
    }

    public void testGetNumberForCall_Unlocked() {
        ContactsPreferencesFactory.setTestInstance(mContactsPreferences);
        CallCardPresenter presenter = new CallCardPresenter();
        presenter.init(getContext(), null);

        assertEquals(NUMBER, presenter.getNumberForCall(mUnlockedContactInfo));
    }

    public void testGetNumberForCall_Locked() {
        ContactsPreferencesFactory.setTestInstance(null);
        CallCardPresenter presenter = new CallCardPresenter();
        presenter.init(getContext(), null);

        assertEquals(LOCATION, presenter.getNumberForCall(mLockedContactInfo));
    }

    public void testGetNumberForCall_EmptyPreferredName() {
        ContactCacheEntry contactInfo = new ContactCacheEntry();
        contactInfo.location = LOCATION;

        ContactsPreferencesFactory.setTestInstance(null);
        CallCardPresenter presenter = new CallCardPresenter();
        presenter.init(getContext(), null);

        assertEquals(LOCATION, presenter.getNumberForCall(contactInfo));
    }
}
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.incallui;

import com.android.dialer.compat.UserManagerCompat;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;

import com.android.contacts.common.preference.ContactsPreferences;

import org.mockito.Mockito;

@SmallTest
public class ContactsPreferencesFactoryTest extends AndroidTestCase {

    public void testNewContactsPreferences_Unlocked() {
        if (!UserManagerCompat.isUserUnlocked(getContext())) {
            return;
        }
        assertNotNull(ContactsPreferencesFactory.newContactsPreferences(getContext()));
    }

    public void testNewContactsPreferences_Locked() {
        if (UserManagerCompat.isUserUnlocked(getContext())) {
            return;
        }
        assertNull(ContactsPreferencesFactory.newContactsPreferences(getContext()));
    }

    public void testNewContactsPreferences_TestInstance() {
        ContactsPreferences testInstance = Mockito.mock(ContactsPreferences.class);
        ContactsPreferencesFactory.setTestInstance(testInstance);
        // Assert that it returns the same object always
        assertSame(testInstance, ContactsPreferencesFactory.newContactsPreferences(getContext()));
        assertSame(testInstance, ContactsPreferencesFactory.newContactsPreferences(getContext()));
    }
}
Loading