feat: SettingsLib验证通过-使用App module启用

This commit is contained in:
2024-12-09 16:04:49 +08:00
parent 1f18a59dab
commit a7f5c61005
1562 changed files with 181632 additions and 18 deletions

View File

@@ -0,0 +1,116 @@
/*
* 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.car.settings.testutils;
import static com.android.car.ui.core.CarUi.requireToolbar;
import android.car.drivingstate.CarUxRestrictions;
import android.os.Bundle;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.car.settings.R;
import com.android.car.settings.common.FragmentHost;
import com.android.car.settings.common.UxRestrictionsProvider;
import com.android.car.ui.toolbar.ToolbarController;
/**
* Test activity used for testing {@code BaseFragment} instances.
*/
public class BaseTestActivity extends FragmentActivity implements FragmentHost,
UxRestrictionsProvider {
private boolean mOnBackPressedFlag;
private CarUxRestrictions mRestrictionInfo = new CarUxRestrictions.Builder(/* reqOpt= */ true,
CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.car_setting_activity);
}
/**
* Places fragment in place of fragment container.
*
* @param fragment Fragment to add to activity.
*/
@Override
public void launchFragment(Fragment fragment) {
if (fragment instanceof DialogFragment) {
throw new IllegalArgumentException(
"cannot launch dialogs with launchFragment() - use showDialog() instead");
}
String tag = Integer.toString(getSupportFragmentManager().getBackStackEntryCount());
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment, tag)
.addToBackStack(null)
.commit();
}
@Override
public void showBlockingMessage() {
// no-op
}
@Override
public ToolbarController getToolbar() {
return requireToolbar(this);
}
@Override
public CarUxRestrictions getCarUxRestrictions() {
return mRestrictionInfo;
}
public void setCarUxRestrictions(CarUxRestrictions restrictionInfo) {
mRestrictionInfo = restrictionInfo;
}
/**
* Override to catch onBackPressed invocations on the activity.
*/
@Override
public void onBackPressed() {
mOnBackPressedFlag = true;
getSupportFragmentManager().popBackStackImmediate();
}
/**
* Gets a boolean flag indicating whether onBackPressed has been called.
*
* @return {@code true} if onBackPressed called, {@code false} otherwise.
*/
public boolean getOnBackPressedFlag() {
return mOnBackPressedFlag;
}
/**
* Clear the boolean flag for onBackPressed by setting it to false.
*/
public void clearOnBackPressedFlag() {
mOnBackPressedFlag = false;
}
@Override
public void goBack() {
onBackPressed();
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.car.settings.testutils;
import android.widget.Button;
import android.widget.TextView;
import androidx.fragment.app.DialogFragment;
/**
* Helper methods for DialogFragment testing.
*/
public class DialogTestUtils {
private DialogTestUtils() {
}
/**
* Invokes onClick on the dialog's positive button.
*/
public static void clickPositiveButton(DialogFragment dialogFragment) {
Button positiveButton = dialogFragment.getDialog().getWindow().findViewById(
com.android.internal.R.id.button1);
positiveButton.callOnClick();
}
/**
* Invokes onClick on the dialog's negative button.
*/
public static void clickNegativeButton(DialogFragment dialogFragment) {
Button negativeButton = dialogFragment.getDialog().getWindow().findViewById(
com.android.internal.R.id.button2);
negativeButton.callOnClick();
}
/**
* Gets dialog's title.
*/
public static String getTitle(DialogFragment dialogFragment) {
TextView titleView = dialogFragment.getDialog().getWindow().findViewById(
com.android.internal.R.id.alertTitle);
return titleView.getText().toString();
}
public static String getMessage(DialogFragment dialogFragment) {
TextView messageView = dialogFragment.getDialog().getWindow().findViewById(
com.android.internal.R.id.message);
return messageView.getText().toString();
}
}

View File

@@ -0,0 +1,106 @@
/*
* 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.car.settings.testutils;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import com.android.car.settings.R;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.android.controller.ComponentController;
/**
* Version of FragmentController that can be used for {@link androidx.fragment.app.Fragment} until
* upstream support is ready.
*/
public class FragmentController<F extends Fragment> extends
ComponentController<FragmentController<F>, F> {
private final F mFragment;
private final ActivityController<BaseTestActivity> mActivityController;
private FragmentController(F fragment) {
super(fragment);
mFragment = fragment;
mActivityController = ActivityController.of(new BaseTestActivity());
}
public static <F extends Fragment> FragmentController<F> of(F fragment) {
return new FragmentController<>(fragment);
}
/**
* Returns the fragment after attaching it to an activity, calling its onCreate() through
* onResume() lifecycle methods and making it visible.
*/
public F setup() {
return create().start().resume().visible().get();
}
/**
* Creates the activity with {@link Bundle} and adds the fragment to it.
*/
public FragmentController<F> create(final Bundle bundle) {
shadowMainLooper.runPaused(
() -> mActivityController
.create(bundle)
.get()
.getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, mFragment)
.commitNow());
return this;
}
@Override
public FragmentController<F> create() {
return create(null);
}
@Override
public FragmentController<F> destroy() {
shadowMainLooper.runPaused(mActivityController::destroy);
return this;
}
public FragmentController<F> start() {
shadowMainLooper.runPaused(mActivityController::start);
return this;
}
public FragmentController<F> resume() {
shadowMainLooper.runPaused(mActivityController::resume);
return this;
}
public FragmentController<F> pause() {
shadowMainLooper.runPaused(mActivityController::pause);
return this;
}
public FragmentController<F> stop() {
shadowMainLooper.runPaused(mActivityController::stop);
return this;
}
public FragmentController<F> visible() {
shadowMainLooper.runPaused(mActivityController::visible);
return this;
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.car.settings.testutils;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorDescription;
import android.os.UserHandle;
import android.util.ArrayMap;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Implements(AccountManager.class)
public class ShadowAccountManager extends org.robolectric.shadows.ShadowAccountManager {
private final Map<Integer, List<Account>> mAccountsAsUserMap = new ArrayMap<>();
private final Map<Integer, List<AuthenticatorDescription>> mAuthenticatorAsUserMap =
new ArrayMap<>();
@Implementation
protected Account[] getAccountsAsUser(int userId) {
if (mAccountsAsUserMap.containsKey(userId)) {
return mAccountsAsUserMap.get(userId).toArray(new Account[]{});
}
return getAccounts();
}
public void addAccountAsUser(int userId, Account account) {
mAccountsAsUserMap.putIfAbsent(userId, new ArrayList<>());
mAccountsAsUserMap.get(userId).add(account);
}
@Implementation
protected Account[] getAccountsByTypeAsUser(String type, UserHandle userHandle) {
return getAccountsByType(type);
}
@Implementation
protected AuthenticatorDescription[] getAuthenticatorTypesAsUser(int userId) {
if (mAuthenticatorAsUserMap.containsKey(userId)) {
return mAuthenticatorAsUserMap.get(userId).toArray(new AuthenticatorDescription[]{});
}
return getAuthenticatorTypes();
}
public void addAuthenticatorAsUser(int userId, AuthenticatorDescription authenticator) {
mAuthenticatorAsUserMap.putIfAbsent(userId, new ArrayList<>());
mAuthenticatorAsUserMap.get(userId).add(authenticator);
}
@Override
public void removeAllAccounts() {
super.removeAllAccounts();
mAccountsAsUserMap.clear();
mAuthenticatorAsUserMap.clear();
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.app.ActivityManager;
import android.content.pm.IPackageDataObserver;
import android.os.UserHandle;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(value = ActivityManager.class)
public class ShadowActivityManager extends org.robolectric.shadows.ShadowActivityManager {
private static final int DEFAULT_CURRENT_USER_ID = UserHandle.USER_SYSTEM;
private static boolean sIsApplicationUserDataCleared;
private static int sCurrentUserId = DEFAULT_CURRENT_USER_ID;
private String mMostRecentlyStoppedPackage;
@Resetter
public static void reset() {
sIsApplicationUserDataCleared = false;
sCurrentUserId = DEFAULT_CURRENT_USER_ID;
}
@Implementation
protected void forceStopPackage(String packageName) {
mMostRecentlyStoppedPackage = packageName;
}
@Implementation
protected boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
return sIsApplicationUserDataCleared;
}
public String getMostRecentlyStoppedPackage() {
return mMostRecentlyStoppedPackage;
}
public static void setApplicationUserDataCleared(boolean applicationUserDataCleared) {
sIsApplicationUserDataCleared = applicationUserDataCleared;
}
@Implementation
protected static int getCurrentUser() {
return sCurrentUserId;
}
public static void setCurrentUser(int userId) {
sCurrentUserId = userId;
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
import android.util.Pair;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Table;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Implements(value = AppOpsManager.class)
public class ShadowAppOpsManager {
private Table<Integer, InternalKey, Integer> mOpToKeyToMode = HashBasedTable.create();
@Implementation
protected void setMode(int code, int uid, String packageName, int mode) {
InternalKey key = new InternalKey(uid, packageName);
mOpToKeyToMode.put(code, key, mode);
}
/** Convenience method to get the mode directly instead of wrapped in an op list. */
public int getMode(int code, int uid, String packageName) {
Integer mode = mOpToKeyToMode.get(code, new InternalKey(uid, packageName));
return mode == null ? AppOpsManager.opToDefaultMode(code) : mode;
}
@Implementation
protected List<PackageOps> getPackagesForOps(int[] ops) {
if (ops == null) {
return Collections.emptyList();
}
ImmutableList.Builder<PackageOps> result = new ImmutableList.Builder<>();
for (int i = 0; i < ops.length; i++) {
int op = ops[i];
Map<InternalKey, Integer> keyToModeMap = mOpToKeyToMode.rowMap().get(op);
if (keyToModeMap == null) {
continue;
}
for (InternalKey key : keyToModeMap.keySet()) {
Integer mode = keyToModeMap.get(key);
if (mode == null) {
mode = AppOpsManager.opToDefaultMode(op);
}
OpEntry opEntry = new OpEntry(op, mode, Collections.emptyMap());
PackageOps packageOp = new PackageOps(key.mPackageName, key.mUid,
Collections.singletonList(opEntry));
result.add(packageOp);
}
}
return result.build();
}
private static class InternalKey {
private int mUid;
private String mPackageName;
InternalKey(int uid, String packageName) {
mUid = uid;
mPackageName = packageName;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof InternalKey) {
InternalKey that = (InternalKey) obj;
return mUid == that.mUid && mPackageName.equals(that.mPackageName);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(mUid, mPackageName);
}
}
}

View File

@@ -0,0 +1,208 @@
/*
* 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.car.settings.testutils;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ApplicationPackageManager;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.Pair;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/** Shadow of ApplicationPackageManager that allows the getting of content providers per user. */
@Implements(value = ApplicationPackageManager.class)
public class ShadowApplicationPackageManager extends
org.robolectric.shadows.ShadowApplicationPackageManager {
private static Resources sResources = null;
private static PackageManager sPackageManager;
private final Map<Integer, String> mUserIdToDefaultBrowserMap = new HashMap<>();
private final Map<String, ComponentName> mPkgToDefaultActivityMap = new HashMap<>();
private final Map<String, IntentFilter> mPkgToDefaultActivityIntentFilterMap = new HashMap<>();
private final Map<IntentFilter, ComponentName> mPreferredActivities = new LinkedHashMap<>();
private final Map<Pair<String, Integer>, Integer> mPkgAndUserIdToIntentVerificationStatusMap =
new HashMap<>();
private List<ResolveInfo> mHomeActivities = Collections.emptyList();
private ComponentName mDefaultHomeActivity;
private String mPermissionControllerPackageName;
@Resetter
public static void reset() {
sResources = null;
sPackageManager = null;
}
@Implementation
@NonNull
protected List<ModuleInfo> getInstalledModules(@PackageManager.ModuleInfoFlags int flags) {
return Collections.emptyList();
}
@Implementation
protected Drawable getUserBadgedIcon(Drawable icon, UserHandle user) {
return icon;
}
@Override
@Implementation
protected ProviderInfo resolveContentProviderAsUser(String name, int flags,
@UserIdInt int userId) {
return resolveContentProvider(name, flags);
}
@Implementation
protected int getPackageUidAsUser(String packageName, int flags, int userId)
throws PackageManager.NameNotFoundException {
return 0;
}
@Implementation
protected void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) {
sPackageManager.deleteApplicationCacheFiles(packageName, observer);
}
@Implementation
protected Resources getResourcesForApplication(String appPackageName)
throws PackageManager.NameNotFoundException {
return sResources;
}
@Implementation
protected List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
return getInstalledApplications(flags);
}
@Implementation
protected ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId)
throws PackageManager.NameNotFoundException {
return getApplicationInfo(packageName, flags);
}
@Implementation
@Override
protected ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
outActivities.addAll(mHomeActivities);
return mDefaultHomeActivity;
}
@Implementation
@Override
protected void clearPackagePreferredActivities(String packageName) {
mPreferredActivities.clear();
}
@Implementation
@Override
public int getPreferredActivities(List<IntentFilter> outFilters,
List<ComponentName> outActivities, String packageName) {
for (IntentFilter filter : mPreferredActivities.keySet()) {
ComponentName name = mPreferredActivities.get(filter);
// If packageName is null, match everything, else filter by packageName.
if (packageName == null) {
outFilters.add(filter);
outActivities.add(name);
} else if (name.getPackageName().equals(packageName)) {
outFilters.add(filter);
outActivities.add(name);
}
}
return 0;
}
@Implementation
@Override
public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set,
ComponentName activity) {
mPreferredActivities.put(filter, activity);
}
@Implementation
@Override
protected String getDefaultBrowserPackageNameAsUser(int userId) {
return mUserIdToDefaultBrowserMap.getOrDefault(userId, null);
}
@Implementation
@Override
protected boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
mUserIdToDefaultBrowserMap.put(userId, packageName);
return true;
}
@Implementation
@Override
protected int getIntentVerificationStatusAsUser(String packageName, int userId) {
Pair<String, Integer> key = new Pair<>(packageName, userId);
return mPkgAndUserIdToIntentVerificationStatusMap.getOrDefault(key,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
}
@Implementation
@Override
protected boolean updateIntentVerificationStatusAsUser(String packageName, int status,
int userId) {
Pair<String, Integer> key = new Pair<>(packageName, userId);
mPkgAndUserIdToIntentVerificationStatusMap.put(key, status);
return true;
}
@Implementation
protected String getPermissionControllerPackageName() {
return mPermissionControllerPackageName;
}
public void setPermissionControllerPackageName(String packageName) {
mPermissionControllerPackageName = packageName;
}
public void setHomeActivities(List<ResolveInfo> homeActivities) {
mHomeActivities = homeActivities;
}
public void setDefaultHomeActivity(ComponentName defaultHomeActivity) {
mDefaultHomeActivity = defaultHomeActivity;
}
public static void setResources(Resources resources) {
sResources = resources;
}
public static void setPackageManager(PackageManager packageManager) {
sPackageManager = packageManager;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.app.Application;
import com.android.settingslib.applications.ApplicationsState;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(ApplicationsState.class)
public class ShadowApplicationsState {
private static ApplicationsState sApplicationsState;
public static void setInstance(ApplicationsState applicationsState) {
sApplicationsState = applicationsState;
}
@Resetter
public static void reset() {
sApplicationsState = null;
}
@Implementation
protected static ApplicationsState getInstance(Application app) {
return sApplicationsState;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.content.Context;
import android.content.pm.ServiceInfo;
import android.service.autofill.AutofillServiceInfo;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(AutofillServiceInfo.class)
public class ShadowAutofillServiceInfo {
private static String sSettingsActivity;
public void __constructor__(Context context, ServiceInfo si) {
// Do nothing when constructed in code.
}
@Resetter
public static void reset() {
sSettingsActivity = null;
}
@Implementation
protected String getSettingsActivity() {
return sSettingsActivity;
}
public static void setSettingsActivity(String settingsActivity) {
sSettingsActivity = settingsActivity;
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.car.settings.testutils;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.os.ParcelUuid;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowApplication;
import java.util.Collections;
import java.util.List;
@Implements(BluetoothAdapter.class)
public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBluetoothAdapter {
private static int sResetCalledCount = 0;
private String mName;
private int mScanMode;
public static boolean verifyFactoryResetCalled(int numTimes) {
return sResetCalledCount == numTimes;
}
@Implementation
protected boolean factoryReset() {
sResetCalledCount++;
return true;
}
@Implementation
protected static synchronized BluetoothAdapter getDefaultAdapter() {
return (BluetoothAdapter) ShadowApplication.getInstance().getBluetoothAdapter();
}
@Implementation
protected ParcelUuid[] getUuids() {
return null;
}
@Implementation
protected String getName() {
return mName;
}
@Implementation
protected boolean setName(String name) {
if (getState() != STATE_ON) {
return false;
}
mName = name;
return true;
}
@Implementation
protected int getScanMode() {
if (getState() != STATE_ON) {
return SCAN_MODE_NONE;
}
return mScanMode;
}
@Implementation
protected Object setScanMode(int scanMode) {
if (getState() != STATE_ON) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
mScanMode = scanMode;
return BluetoothStatusCodes.SUCCESS;
}
@Implementation
protected List<Integer> getSupportedProfiles() {
return Collections.emptyList();
}
@Resetter
public static void reset() {
sResetCalledCount = 0;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 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.car.settings.testutils;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(BluetoothPan.class)
public class ShadowBluetoothPan {
@Implementation
public void __constructor__(Context context, BluetoothProfile.ServiceListener l) {
// Do nothing. Implemented to avoid NullPointerException in BluetoothPan.
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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.car.settings.testutils;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.car.Car;
import android.car.CarNotConnectedException;
import android.content.Context;
import android.content.ServiceConnection;
import org.mockito.stubbing.Answer;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
/**
* Shadow class for {@link Car}. Components in car support library expects
* this class to be available at run time.
*/
@Implements(Car.class)
public class ShadowCar {
private static Car sMockCar = mock(Car.class);
private static boolean sIsConnected;
private static String sServiceName;
private static Object sCarManager;
/**
* Returns a mocked version of a {@link Car} object.
*/
@Implementation
protected static Car createCar(Context context, ServiceConnection serviceConnection) {
if (serviceConnection != null) {
doAnswer((Answer<Void>) invocation -> {
serviceConnection.onServiceConnected(null, null);
return null;
}).when(sMockCar).connect();
doAnswer((Answer<Void>) invocation -> {
serviceConnection.onServiceDisconnected(null);
return null;
}).when(sMockCar).disconnect();
}
doReturn(sIsConnected).when(sMockCar).isConnected();
if (sServiceName != null) {
try {
doReturn(sCarManager).when(sMockCar).getCarManager(sServiceName);
} catch (CarNotConnectedException e) {
// do nothing, have to do this because compiler doesn't understand mock can't throw
// exception.
}
}
return sMockCar;
}
/**
* Returns a mocked version of a {@link Car} object.
*/
@Implementation
protected static Car createCar(Context context) {
doReturn(sIsConnected).when(sMockCar).isConnected();
if (sServiceName != null) {
try {
doReturn(sCarManager).when(sMockCar).getCarManager(sServiceName);
} catch (CarNotConnectedException e) {
// do nothing, have to do this because compiler doesn't understand mock can't throw
// exception.
}
}
return sMockCar;
}
/**
* Sets the manager returned by {@link Car#getCarManager(String)}.
*
* @param serviceName the name for the service request that should return this car manager.
* @param carManager the object returned by a call with this service.
*/
public static void setCarManager(String serviceName, Object carManager) {
sServiceName = serviceName;
sCarManager = carManager;
try {
doReturn(carManager).when(sMockCar).getCarManager(serviceName);
} catch (CarNotConnectedException e) {
// do nothing, have to do this because compiler doesn't understand mock can't throw e.
}
}
/**
* Resets the shadow state, note this will not remove stubbed behavior on references to older
* calls to {@link #createCar(Context, ServiceConnection)}.
*/
@Resetter
public static void reset() {
sMockCar = mock(Car.class);
sServiceName = null;
sCarManager = null;
sIsConnected = false;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import com.android.car.settings.units.CarUnitsManager;
import com.android.car.settings.units.Unit;
import com.android.car.settings.units.UnitsMap;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
/**
* Shadow class for {@link CarUnitsManager}.
*/
@Implements(CarUnitsManager.class)
public class ShadowCarUnitsManager {
private static boolean sConnected = false;
private static CarUnitsManager.OnCarServiceListener sListener;
private static HashMap<Integer, Unit[]> sSupportedUnits = new HashMap<>();
private static HashMap<Integer, Unit> sUnitsBeingUsed = new HashMap<>();
@Implementation
protected void connect() {
sConnected = true;
}
@Implementation
protected void disconnect() {
sConnected = false;
}
@Implementation
protected static Unit[] getUnitsSupportedByProperty(int propertyId) {
return sSupportedUnits.get(propertyId);
}
@Implementation
protected static Unit getUnitUsedByProperty(int propertyId) {
return sUnitsBeingUsed.get(propertyId);
}
@Implementation
protected static void setUnitUsedByProperty(int propertyId, int unitId) {
sUnitsBeingUsed.put(propertyId, UnitsMap.MAP.get(unitId));
}
@Implementation
protected static void registerCarServiceListener(
CarUnitsManager.OnCarServiceListener listener) {
sListener = listener;
}
@Implementation
protected static void unregisterCarServiceListener() {
sListener = null;
}
@Resetter
public static void reset() {
sConnected = false;
sListener = null;
sSupportedUnits = new HashMap<>();
sUnitsBeingUsed = new HashMap<>();
}
public static void setUnitsSupportedByProperty(int propertyId, Unit[] units) {
sSupportedUnits.put(propertyId, units);
}
public static boolean isConnected() {
return sConnected;
}
public static CarUnitsManager.OnCarServiceListener getListener() {
return sListener;
}
}

View File

@@ -0,0 +1,141 @@
/*
* 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.car.settings.testutils;
import android.content.Context;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiManager;
import com.android.car.settings.wifi.CarWifiManager;
import com.android.wifitrackerlib.WifiEntry;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.List;
/** TODO(b/148971715): Refactor all methods to run without relying on sInstance. */
@Implements(CarWifiManager.class)
public class ShadowCarWifiManager {
public static final int STATE_UNKNOWN = -1;
public static final int STATE_STARTED = 0;
public static final int STATE_STOPPED = 1;
public static final int STATE_DESTROYED = 2;
private static CarWifiManager sInstance;
private static int sCurrentState = STATE_UNKNOWN;
private static SoftApConfiguration sSoftApConfiguration =
new SoftApConfiguration.Builder().build();
private static boolean sIs5GhzBandSupported = true;
private static int sWifiState = WifiManager.WIFI_STATE_UNKNOWN;
public static void setInstance(CarWifiManager wifiManager) {
sInstance = wifiManager;
}
@Resetter
public static void reset() {
sInstance = null;
sSoftApConfiguration = new SoftApConfiguration.Builder().build();
sCurrentState = STATE_UNKNOWN;
sIs5GhzBandSupported = true;
sWifiState = WifiManager.WIFI_STATE_UNKNOWN;
}
@Implementation
public void __constructor__(Context context) {
}
@Implementation
public void start() {
sCurrentState = STATE_STARTED;
}
@Implementation
public void stop() {
sCurrentState = STATE_STOPPED;
}
@Implementation
public void destroy() {
sCurrentState = STATE_DESTROYED;
}
@Implementation
public void setSoftApConfig(SoftApConfiguration config) {
sSoftApConfiguration = config;
}
@Implementation
public SoftApConfiguration getSoftApConfig() {
return sSoftApConfiguration;
}
@Implementation
public boolean isWifiEnabled() {
return sInstance.isWifiEnabled();
}
@Implementation
public int getWifiState() {
return sWifiState;
}
public static void setWifiState(int wifiState) {
sWifiState = wifiState;
}
@Implementation
public boolean isWifiApEnabled() {
return sInstance.isWifiApEnabled();
}
@Implementation
public List<WifiEntry> getAllWifiEntries() {
return sInstance.getAllWifiEntries();
}
@Implementation
public List<WifiEntry> getSavedWifiEntries() {
return sInstance.getSavedWifiEntries();
}
@Implementation
protected String getCountryCode() {
return "1";
}
@Implementation
protected boolean is5GhzBandSupported() {
return sIs5GhzBandSupported;
}
@Implementation
protected boolean addListener(CarWifiManager.Listener listener) {
return sInstance.addListener(listener);
}
public static void setIs5GhzBandSupported(boolean supported) {
sIs5GhzBandSupported = supported;
}
public static int getCurrentState() {
return sCurrentState;
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.car.settings.testutils;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.util.SparseArray;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(CarrierConfigManager.class)
public class ShadowCarrierConfigManager {
private SparseArray<PersistableBundle> mBundles = new SparseArray<>();
@Implementation
protected PersistableBundle getConfigForSubId(int subId) {
return mBundles.get(subId);
}
public void setConfigForSubId(int subId, PersistableBundle config) {
mBundles.put(subId, config);
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.car.settings.testutils;
import static org.mockito.Mockito.mock;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Handler;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
import java.util.Map;
@Implements(ConnectivityManager.class)
public class ShadowConnectivityManager extends org.robolectric.shadows.ShadowConnectivityManager {
private static int sResetCalledCount = 0;
private final Map<Network, NetworkCapabilities> mCapabilitiesMap = new HashMap<>();
private int mStartTetheringCalledCount = 0;
private int mStopTetheringCalledCount = 0;
private int mTetheringType;
public static boolean verifyFactoryResetCalled(int numTimes) {
return sResetCalledCount == numTimes;
}
public boolean verifyStartTetheringCalled(int numTimes) {
return mStartTetheringCalledCount == numTimes;
}
public boolean verifyStopTetheringCalled(int numTimes) {
return mStopTetheringCalledCount == numTimes;
}
public int getTetheringType() {
return mTetheringType;
}
public void addNetworkCapabilities(Network network, NetworkCapabilities capabilities) {
super.addNetwork(network, mock(NetworkInfo.class));
mCapabilitiesMap.put(network, capabilities);
}
@Implementation
protected NetworkCapabilities getNetworkCapabilities(Network network) {
return mCapabilitiesMap.get(network);
}
@Implementation
public void startTethering(int type, boolean showProvisioningUi,
final ConnectivityManager.OnStartTetheringCallback callback, Handler handler) {
mTetheringType = type;
mStartTetheringCalledCount++;
}
@Implementation
public void stopTethering(int type) {
mTetheringType = type;
mStopTetheringCalledCount++;
}
@Implementation
protected void factoryReset() {
sResetCalledCount++;
}
@Resetter
public static void reset() {
sResetCalledCount = 0;
}
}

View File

@@ -0,0 +1,174 @@
/*
* 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.car.settings.testutils;
import android.accounts.Account;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.SyncAdapterType;
import android.content.SyncInfo;
import android.content.SyncStatusInfo;
import android.content.SyncStatusObserver;
import android.os.Bundle;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Derived from {@link com.android.settings.testutils.shadow.ShadowContentResolver}
*
* <p>Needed for many account-related tests because the default ShadowContentResolver does not
* include an implementation of getSyncAdapterTypesAsUser, which is used by {@link
* com.android.settingslib.accounts.AuthenticatorHelper#buildAccountTypeToAuthoritiesMap}.
*/
@Implements(ContentResolver.class)
public class ShadowContentResolver extends org.robolectric.shadows.ShadowContentResolver {
private static final int SYNCABLE = 1;
private static SyncAdapterType[] sSyncAdapterTypes = new SyncAdapterType[0];
private static Map<String, Integer> sSyncable = new HashMap<>();
private static Map<String, Boolean> sSyncAutomatically = new HashMap<>();
private static Map<Integer, Boolean> sMasterSyncAutomatically = new HashMap<>();
private static Map<String, SyncStatusInfo> sSyncStatus = new HashMap<>();
private static List<SyncInfo> sSyncs = new ArrayList<>();
private static SyncListener sSyncListener;
private static SyncStatusObserver sStatusObserver;
@Implementation
protected static SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
return sSyncAdapterTypes;
}
@Implementation
protected static int getIsSyncableAsUser(Account account, String authority, int userId) {
return sSyncable.getOrDefault(authority, SYNCABLE);
}
@Implementation
protected static boolean getSyncAutomaticallyAsUser(Account account, String authority,
int userId) {
return sSyncAutomatically.getOrDefault(authority, true);
}
@Implementation
protected static boolean getMasterSyncAutomaticallyAsUser(int userId) {
return sMasterSyncAutomatically.getOrDefault(userId, true);
}
@Implementation
protected static List<SyncInfo> getCurrentSyncsAsUser(@UserIdInt int userId) {
return sSyncs;
}
@Implementation
protected static SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
@UserIdInt int userId) {
return sSyncStatus.get(authority);
}
public static void setSyncAdapterTypes(SyncAdapterType[] syncAdapterTypes) {
sSyncAdapterTypes = syncAdapterTypes;
}
@Implementation
public static void setIsSyncable(Account account, String authority, int syncable) {
sSyncable.put(authority, syncable);
}
@Implementation
protected static void setSyncAutomaticallyAsUser(Account account, String authority,
boolean sync, @UserIdInt int userId) {
sSyncAutomatically.put(authority, sync);
}
@Implementation
protected static void setMasterSyncAutomaticallyAsUser(boolean sync, @UserIdInt int userId) {
sMasterSyncAutomatically.put(userId, sync);
}
public static void setCurrentSyncs(List<SyncInfo> syncs) {
sSyncs = syncs;
}
public static void setSyncStatus(Account account, String authority, SyncStatusInfo status) {
sSyncStatus.put(authority, status);
}
@Implementation
public static void cancelSyncAsUser(Account account, String authority, @UserIdInt int userId) {
if (sSyncListener != null) {
sSyncListener.onSyncCanceled(account, authority, userId);
}
}
@Implementation
public static void requestSyncAsUser(Account account, String authority, @UserIdInt int userId,
Bundle extras) {
if (sSyncListener != null) {
sSyncListener.onSyncRequested(account, authority, userId, extras);
}
}
public static void setSyncListener(SyncListener syncListener) {
sSyncListener = syncListener;
}
@Implementation
protected static Object addStatusChangeListener(int mask, SyncStatusObserver callback) {
sStatusObserver = callback;
return null;
}
@Implementation
protected static void removeStatusChangeListener(Object handle) {
sStatusObserver = null;
}
public static SyncStatusObserver getStatusChangeListener() {
return sStatusObserver;
}
@Resetter
public static void reset() {
org.robolectric.shadows.ShadowContentResolver.reset();
sSyncable.clear();
sSyncAutomatically.clear();
sMasterSyncAutomatically.clear();
sSyncAdapterTypes = new SyncAdapterType[0];
sSyncStatus.clear();
sSyncs = new ArrayList<>();
sSyncListener = null;
sStatusObserver = null;
}
/**
* A listener interface that can be used to verify calls to {@link #cancelSyncAsUser} and {@link
* #requestSyncAsUser}
*/
public interface SyncListener {
void onSyncCanceled(Account account, String authority, @UserIdInt int userId);
void onSyncRequested(Account account, String authority, @UserIdInt int userId,
Bundle extras);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.net.NetworkTemplate;
import com.android.settingslib.net.DataUsageController;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(DataUsageController.class)
public class ShadowDataUsageController {
private static DataUsageController sInstance;
public static void setInstance(DataUsageController dataUsageController) {
sInstance = dataUsageController;
}
@Implementation
protected DataUsageController.DataUsageInfo getDataUsageInfo(NetworkTemplate template) {
return sInstance.getDataUsageInfo(template);
}
@Resetter
public static void reset() {
sInstance = null;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.content.Context;
import android.telecom.DefaultDialerManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(DefaultDialerManager.class)
public class ShadowDefaultDialerManager {
private static String sDefaultDialerPackage;
@Resetter
public static void reset() {
sDefaultDialerPackage = null;
}
@Implementation
protected static String getDefaultDialerApplication(Context context) {
return sDefaultDialerPackage;
}
public static void setDefaultDialerApplication(String defaultDialerPackage) {
sDefaultDialerPackage = defaultDialerPackage;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.util.ArraySet;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.List;
import java.util.Set;
@Implements(value = DevicePolicyManager.class)
public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDevicePolicyManager {
@Nullable
private static List<String> sPermittedInputMethods;
private Set<String> mActiveAdminsPackages = new ArraySet<>();
private boolean mIsInstallInQueue;
@Resetter
public static void reset() {
sPermittedInputMethods = null;
}
@Implementation
@Nullable
protected List<String> getPermittedInputMethodsForCurrentUser() {
return sPermittedInputMethods;
}
public static void setPermittedInputMethodsForCurrentUser(@Nullable List<String> inputMethods) {
sPermittedInputMethods = inputMethods;
}
@Implementation
protected boolean packageHasActiveAdmins(String packageName) {
return mActiveAdminsPackages.contains(packageName);
}
public void setPackageHasActiveAdmins(String packageName, boolean hasActiveAdmins) {
if (hasActiveAdmins) {
mActiveAdminsPackages.add(packageName);
} else {
mActiveAdminsPackages.remove(packageName);
}
}
@Implementation
protected boolean isUninstallInQueue(String packageName) {
return mIsInstallInQueue;
}
public void setIsUninstallInQueue(boolean isUninstallInQueue) {
mIsInstallInQueue = isUninstallInQueue;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.hardware.usb.IUsbManager;
import android.os.IBinder;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(value = IUsbManager.Stub.class)
public class ShadowIUsbManager {
private static IUsbManager sInstance;
public static void setInstance(IUsbManager instance) {
sInstance = instance;
}
@Implementation
public static IUsbManager asInterface(IBinder obj) {
return sInstance;
}
@Resetter
public static void reset() {
sInstance = null;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.content.pm.ApplicationInfo;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.IconDrawableFactory;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(value = IconDrawableFactory.class)
public class ShadowIconDrawableFactory {
@Implementation
protected Drawable getBadgedIcon(ApplicationInfo appInfo) {
return new ColorDrawable(0);
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.provider.Settings;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import androidx.annotation.Nullable;
import com.android.car.settings.inputmethod.InputMethodUtil;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Implements(value = InputMethodManager.class)
public class ShadowInputMethodManager extends org.robolectric.shadows.ShadowInputMethodManager {
private static List<InputMethodSubtype> sInputMethodSubtypes = new ArrayList<>();
private static List<InputMethodInfo> sInputMethodList = new ArrayList<>();
private static Map<String, InputMethodInfo> sInputMethodMap = new HashMap<>();
@Resetter
public static void reset() {
sInputMethodSubtypes.clear();
sInputMethodList.clear();
sInputMethodMap.clear();
org.robolectric.shadows.ShadowInputMethodManager.reset();
}
public static void setEnabledInputMethodSubtypeList(List<InputMethodSubtype> list) {
sInputMethodSubtypes = list;
}
@Implementation
protected List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
boolean allowsImplicitlySelectedSubtypes) {
return sInputMethodSubtypes;
}
public static void setEnabledInputMethodList(@Nullable List<InputMethodInfo> list) {
if (list == null || list.size() == 0) {
Settings.Secure.putString(RuntimeEnvironment.application.getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS, "");
return;
}
String concatenatedInputMethodIds = createInputMethodIdString(list.stream().map(
imi -> imi.getId()).collect(Collectors.toList()).toArray(new String[list.size()]));
Settings.Secure.putString(RuntimeEnvironment.application.getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS, concatenatedInputMethodIds);
addInputMethodInfosToMap(list);
}
@Implementation
protected List<InputMethodInfo> getEnabledInputMethodList() {
List<InputMethodInfo> enabledInputMethodList = new ArrayList<>();
String inputMethodIdString = Settings.Secure.getString(
RuntimeEnvironment.application.getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS);
if (inputMethodIdString == null || inputMethodIdString.isEmpty()) {
return enabledInputMethodList;
}
InputMethodUtil.sInputMethodSplitter.setString(inputMethodIdString);
while (InputMethodUtil.sInputMethodSplitter.hasNext()) {
enabledInputMethodList.add(sInputMethodMap.get(InputMethodUtil.sInputMethodSplitter
.next()));
}
return enabledInputMethodList;
}
public void setInputMethodList(List<InputMethodInfo> inputMethodInfos) {
sInputMethodList = inputMethodInfos;
if (inputMethodInfos == null) {
return;
}
addInputMethodInfosToMap(inputMethodInfos);
}
@Implementation
protected List<InputMethodInfo> getInputMethodList() {
return sInputMethodList;
}
private static String createInputMethodIdString(String... ids) {
int size = ids == null ? 0 : ids.length;
if (size == 1) {
return ids[0];
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < size; i++) {
builder.append(ids[i]);
if (i != size - 1) {
builder.append(InputMethodUtil.INPUT_METHOD_DELIMITER);
}
}
return builder.toString();
}
private static void addInputMethodInfosToMap(List<InputMethodInfo> inputMethodInfos) {
if (sInputMethodMap == null || sInputMethodMap.size() == 0) {
sInputMethodMap = inputMethodInfos.stream().collect(Collectors.toMap(
InputMethodInfo::getId, imi -> imi));
return;
}
inputMethodInfos.forEach(imi -> {
sInputMethodMap.put(imi.getId(), imi);
});
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.bluetooth.BluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(LocalBluetoothAdapter.class)
public class ShadowLocalBluetoothAdapter {
private int mState = BluetoothAdapter.STATE_OFF;
private boolean mIsBluetoothEnabled = true;
private int mScanMode = BluetoothAdapter.SCAN_MODE_NONE;
@Implementation
protected boolean isEnabled() {
return mIsBluetoothEnabled;
}
@Implementation
protected boolean enable() {
mIsBluetoothEnabled = true;
return true;
}
@Implementation
protected boolean disable() {
mIsBluetoothEnabled = false;
return true;
}
@Implementation
protected int getScanMode() {
return mScanMode;
}
@Implementation
protected void setScanMode(int mode) {
mScanMode = mode;
}
@Implementation
protected boolean setScanMode(int mode, int duration) {
mScanMode = mode;
return true;
}
@Implementation
protected int getState() {
return mState;
}
public void setState(int state) {
mState = state;
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.car.settings.testutils;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@Implements(LocalBroadcastManager.class)
public class ShadowLocalBroadcastManager {
private static List<Intent> sSentBroadcastIntents = new ArrayList<>();
private static List<Wrapper> sRegisteredReceivers = new ArrayList<>();
@Implementation
public static LocalBroadcastManager getInstance(final Context context) {
return ShadowApplication.getInstance().getSingleton(LocalBroadcastManager.class,
() -> ReflectionHelpers.callConstructor(LocalBroadcastManager.class,
ClassParameter.from(Context.class, context)));
}
@Implementation
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
sRegisteredReceivers.add(new Wrapper(receiver, filter));
}
@Implementation
public void unregisterReceiver(BroadcastReceiver receiver) {
Iterator<Wrapper> iterator = sRegisteredReceivers.iterator();
while (iterator.hasNext()) {
Wrapper wrapper = iterator.next();
if (wrapper.getBroadcastReceiver() == receiver) {
iterator.remove();
}
}
}
@Implementation
public boolean sendBroadcast(Intent intent) {
boolean sent = false;
sSentBroadcastIntents.add(intent);
List<Wrapper> copy = new ArrayList<>(sRegisteredReceivers);
for (Wrapper wrapper : copy) {
if (wrapper.getIntentFilter().matchAction(intent.getAction())) {
int match = wrapper.getIntentFilter().matchData(intent.getType(),
intent.getScheme(), intent.getData());
if (match != IntentFilter.NO_MATCH_DATA && match != IntentFilter.NO_MATCH_TYPE) {
sent = true;
final BroadcastReceiver receiver = wrapper.getBroadcastReceiver();
final Intent broadcastIntent = intent;
Robolectric.getForegroundThreadScheduler().post(
(Runnable) () -> receiver.onReceive(RuntimeEnvironment.application,
broadcastIntent));
}
}
}
return sent;
}
@Resetter
public static void reset() {
sSentBroadcastIntents.clear();
sRegisteredReceivers.clear();
}
public static List<Intent> getSentBroadcastIntents() {
return sSentBroadcastIntents;
}
public static List<Wrapper> getRegisteredBroadcastReceivers() {
return sRegisteredReceivers;
}
public static class Wrapper {
private final BroadcastReceiver mBroadcastReceiver;
private final IntentFilter mIntentFilter;
public Wrapper(BroadcastReceiver broadcastReceiver, IntentFilter intentFilter) {
this.mBroadcastReceiver = broadcastReceiver;
this.mIntentFilter = intentFilter;
}
public BroadcastReceiver getBroadcastReceiver() {
return mBroadcastReceiver;
}
public IntentFilter getIntentFilter() {
return mIntentFilter;
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.car.settings.testutils;
import android.content.Context;
import com.android.internal.app.LocaleStore;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@Implements(LocaleStore.class)
public class ShadowLocaleStore {
private static Map<LocaleStore.LocaleInfo, Set<LocaleStore.LocaleInfo>>
sLocaleInfoRelationships = new HashMap<>();
public static void addLocaleRelationship(Locale parent, Locale child) {
LocaleStore.LocaleInfo parentInfo = LocaleStore.getLocaleInfo(parent);
LocaleStore.LocaleInfo childInfo = LocaleStore.getLocaleInfo(child);
Set<LocaleStore.LocaleInfo> set = sLocaleInfoRelationships.getOrDefault(parentInfo,
new HashSet<>());
set.add(childInfo);
sLocaleInfoRelationships.put(parentInfo, set);
}
@Resetter
public static void reset() {
sLocaleInfoRelationships.clear();
}
@Implementation
protected static Set<LocaleStore.LocaleInfo> getLevelLocales(Context context,
Set<String> ignorables, LocaleStore.LocaleInfo parent, boolean translatedOnly) {
if (parent != null) {
return sLocaleInfoRelationships.getOrDefault(parent, new HashSet<>());
} else {
return sLocaleInfoRelationships.keySet();
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.car.settings.testutils;
import android.content.Intent;
import android.location.LocationManager;
import android.os.UserHandle;
import android.provider.Settings;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(value = LocationManager.class)
public class ShadowLocationManager {
@Implementation
protected void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
int newMode = enabled
? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
: Settings.Secure.LOCATION_MODE_OFF;
Settings.Secure.putIntForUser(RuntimeEnvironment.application.getContentResolver(),
Settings.Secure.LOCATION_MODE, newMode, userHandle.getIdentifier());
RuntimeEnvironment.application.sendBroadcast(new Intent(
LocationManager.MODE_CHANGED_ACTION));
}
@Implementation
protected boolean isLocationEnabled() {
return Settings.Secure.getInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF)
!= Settings.Secure.LOCATION_MODE_OFF;
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.car.settings.testutils;
import android.app.admin.DevicePolicyManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
/**
* Shadow for LockPatternUtils.
*/
@Implements(LockPatternUtils.class)
public class ShadowLockPatternUtils {
public static final int NO_USER = -1;
private static int sPasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
private static byte[] sSavedPassword;
private static byte[] sSavedPattern;
private static LockscreenCredential sClearLockCredential;
private static int sClearLockUser = NO_USER;
@Resetter
public static void reset() {
sPasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
sSavedPassword = null;
sSavedPattern = null;
sClearLockCredential = null;
sClearLockUser = NO_USER;
}
/**
* Sets the current password quality that is returned by
* {@link LockPatternUtils#getKeyguardStoredPasswordQuality}.
*/
public static void setPasswordQuality(int passwordQuality) {
sPasswordQuality = passwordQuality;
}
/**
* Returns the password saved by a call to {@link LockPatternUtils#saveLockPassword}.
*/
public static byte[] getSavedPassword() {
return sSavedPassword;
}
/**
* Returns the saved credential passed in to clear the lock, null if it has not been cleared.
*/
public static LockscreenCredential getClearLockCredential() {
return sClearLockCredential;
}
/**
* Returns the user passed in to clear the lock, {@link #NO_USER} if it has not been cleared.
*/
public static int getClearLockUser() {
return sClearLockUser;
}
/**
* Returns the pattern saved by a call to {@link LockPatternUtils#saveLockPattern}.
*/
public static byte[] getSavedPattern() {
return sSavedPattern;
}
@Implementation
protected int getKeyguardStoredPasswordQuality(int userHandle) {
return sPasswordQuality;
}
@Implementation
public boolean setLockCredential(LockscreenCredential newCredential,
LockscreenCredential savedCredential, int userId) {
if (newCredential.isPassword() || newCredential.isPin()) {
sSavedPassword = newCredential.duplicate().getCredential();
} else if (newCredential.isPattern()) {
sSavedPattern = newCredential.duplicate().getCredential();
} else if (newCredential.isNone()) {
sClearLockCredential = savedCredential.duplicate();
sClearLockUser = userId;
}
return true;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2011 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.car.settings.testutils;
import android.net.NetworkPolicy;
import android.net.NetworkTemplate;
import com.android.settingslib.NetworkPolicyEditor;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(NetworkPolicyEditor.class)
public class ShadowNetworkPolicyEditor {
private static NetworkPolicy sNetworkPolicy;
@Implementation
public NetworkPolicy getPolicy(NetworkTemplate template) {
return sNetworkPolicy;
}
public static void setNetworkPolicy(NetworkPolicy networkPolicy) {
sNetworkPolicy = networkPolicy;
}
@Resetter
public static void reset() {
sNetworkPolicy = null;
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.car.settings.testutils;
import android.content.Context;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.util.Pair;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@Implements(NetworkPolicyManager.class)
public class ShadowNetworkPolicyManager {
private static NetworkPolicyManager sNetworkPolicyManager;
private static Iterator<Pair<ZonedDateTime, ZonedDateTime>> sCycleIterator;
private static Map<String, Integer> sResetCalledForSubscriberCount = new HashMap<>();
public static boolean verifyFactoryResetCalled(String subscriber, int numTimes) {
if (!sResetCalledForSubscriberCount.containsKey(subscriber)) return false;
return sResetCalledForSubscriberCount.get(subscriber) == numTimes;
}
@Implementation
protected void factoryReset(String subscriber) {
sResetCalledForSubscriberCount.put(subscriber,
sResetCalledForSubscriberCount.getOrDefault(subscriber, 0) + 1);
}
@Implementation
protected int[] getUidsWithPolicy(int policy) {
return sNetworkPolicyManager == null ? new int[0] : sNetworkPolicyManager
.getUidsWithPolicy(policy);
}
@Implementation
protected static Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator(
NetworkPolicy policy) {
return sCycleIterator;
}
public static void setCycleIterator(
Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator) {
sCycleIterator = cycleIterator;
}
@Implementation
public static NetworkPolicyManager from(Context context) {
return sNetworkPolicyManager;
}
public static void setNetworkPolicyManager(NetworkPolicyManager networkPolicyManager) {
sNetworkPolicyManager = networkPolicyManager;
}
@Resetter
public static void reset() {
sResetCalledForSubscriberCount.clear();
sCycleIterator = null;
sNetworkPolicyManager = null;
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.car.settings.testutils;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
import android.permission.PermissionControllerManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import java.util.List;
/**
* A RuntimePermissionPresenter that changes nothing and <u>never returns callbacks</u>.
*/
@Implements(PermissionControllerManager.class)
public class ShadowPermissionControllerManager {
@Implementation
protected void __constructor__(Context context, @NonNull Handler handler) {
// no nothing, everything is shadowed
}
@Implementation
protected void getAppPermissions(String packageName,
PermissionControllerManager.OnGetAppPermissionResultCallback callback,
Handler handler) {
}
@Implementation
protected void revokeRuntimePermission(String packageName, String permissionName) {
}
@Implementation
protected void countPermissionApps(List<String> permissionNames, boolean countOnlyGranted,
boolean countSystem,
PermissionControllerManager.OnCountPermissionAppsResultCallback callback,
Handler handler) {
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.car.settings.testutils;
import android.content.Context;
import android.os.RecoverySystem;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(RecoverySystem.class)
public class ShadowRecoverySystem {
private static int sWipeEuiccDataCalledCount = 0;
public static boolean verifyWipeEuiccDataCalled(int numTimes) {
return sWipeEuiccDataCalledCount == numTimes;
}
@Implementation
protected static boolean wipeEuiccData(Context context, final String packageName) {
sWipeEuiccDataCalledCount++;
return true;
}
@Resetter
public static void reset() {
sWipeEuiccDataCalledCount = 0;
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.content.Context;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(RestrictedLockUtilsInternal.class)
public class ShadowRestrictedLockUtilsInternal {
private static RestrictedLockUtils.EnforcedAdmin sEnforcedAdmin;
private static boolean sHasBaseUserRestriction;
@Resetter
public static void reset() {
sEnforcedAdmin = null;
sHasBaseUserRestriction = false;
}
public static void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
sEnforcedAdmin = enforcedAdmin;
}
public static void setHasBaseUserRestriction(boolean hasBaseUserRestriction) {
sHasBaseUserRestriction = hasBaseUserRestriction;
}
public static void sendShowAdminSupportDetailsIntent(Context context,
RestrictedLockUtils.EnforcedAdmin admin) {
// do nothing
}
@Implementation
protected static RestrictedLockUtils.EnforcedAdmin checkIfRestrictionEnforced(Context context,
String userRestriction, int userId) {
return sEnforcedAdmin;
}
@Implementation
protected static boolean hasBaseUserRestriction(Context context,
String userRestriction, int userId) {
return sHasBaseUserRestriction;
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.car.settings.testutils;
import android.content.Context;
import android.media.Ringtone;
import android.net.Uri;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
import java.util.Map;
@Implements(Ringtone.class)
public class ShadowRingtone {
private static Map<Uri, String> sTitleForGivenUri = new HashMap<>();
public static void setExpectedTitleForUri(Uri uri, String title) {
sTitleForGivenUri.put(uri, title);
}
@Implementation
protected static String getTitle(
Context context, Uri uri, boolean followSettingsUri, boolean allowRemote) {
return sTitleForGivenUri.getOrDefault(uri, null);
}
@Resetter
public static void reset() {
sTitleForGivenUri.clear();
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.car.settings.testutils;
import android.content.Context;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
import java.util.Map;
@Implements(RingtoneManager.class)
public class ShadowRingtoneManager {
private static Ringtone sRingtone;
private static Map<Integer, Uri> sSetDefaultRingtoneCalled = new HashMap<>();
public static void setRingtone(Ringtone ringtone) {
sRingtone = ringtone;
}
@Implementation
protected static Ringtone getRingtone(final Context context, Uri ringtoneUri) {
return sRingtone;
}
@Implementation
public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
sSetDefaultRingtoneCalled.put(type, ringtoneUri);
}
@Implementation
public static Uri getActualDefaultRingtoneUri(Context context, int type) {
return sSetDefaultRingtoneCalled.getOrDefault(type, null);
}
@Resetter
public static void reset() {
sRingtone = null;
sSetDefaultRingtoneCalled.clear();
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.car.settings.testutils;
import android.content.ContentResolver;
import android.provider.Settings;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowSettings;
import org.robolectric.util.ReflectionHelpers;
@Implements(Settings.Secure.class)
public class ShadowSecureSettings extends ShadowSettings.ShadowSecure {
@Implementation
protected static int getInt(ContentResolver resolver, String name) {
return Shadow.directlyOn(
Settings.Secure.class,
"getInt",
ReflectionHelpers.ClassParameter.from(ContentResolver.class, resolver),
ReflectionHelpers.ClassParameter.from(String.class, name));
}
@Implementation
protected static int getInt(ContentResolver resolver, String name, int def) {
return Shadow.directlyOn(
Settings.Secure.class,
"getInt",
ReflectionHelpers.ClassParameter.from(ContentResolver.class, resolver),
ReflectionHelpers.ClassParameter.from(String.class, name),
ReflectionHelpers.ClassParameter.from(int.class, def));
}
@Implementation
protected static int getIntForUser(ContentResolver resolver, String name, int def,
int userHandle) {
return Shadow.directlyOn(
Settings.Secure.class,
"getIntForUser",
ReflectionHelpers.ClassParameter.from(ContentResolver.class, resolver),
ReflectionHelpers.ClassParameter.from(String.class, name),
ReflectionHelpers.ClassParameter.from(int.class, def),
ReflectionHelpers.ClassParameter.from(int.class, userHandle));
}
@Implementation
protected static boolean putInt(ContentResolver resolver, String name, int value) {
return Shadow.directlyOn(
Settings.Secure.class,
"putInt",
ReflectionHelpers.ClassParameter.from(ContentResolver.class, resolver),
ReflectionHelpers.ClassParameter.from(String.class, name),
ReflectionHelpers.ClassParameter.from(int.class, value));
}
@Implementation
protected static boolean putIntForUser(ContentResolver resolver, String name, int value,
int userHandle) {
return Shadow.directlyOn(
Settings.Secure.class,
"putIntForUser",
ReflectionHelpers.ClassParameter.from(ContentResolver.class, resolver),
ReflectionHelpers.ClassParameter.from(String.class, name),
ReflectionHelpers.ClassParameter.from(int.class, value),
ReflectionHelpers.ClassParameter.from(int.class, userHandle));
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.content.ComponentName;
import android.content.Context;
import com.android.internal.telephony.SmsApplication;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(SmsApplication.class)
public class ShadowSmsApplication {
private static ComponentName sDefaultSmsApplication;
@Resetter
public static void reset() {
sDefaultSmsApplication = null;
}
@Implementation
protected static ComponentName getDefaultSmsApplication(
Context context, boolean updateIfNeeded) {
return sDefaultSmsApplication;
}
public static void setDefaultSmsApplication(ComponentName defaultSmsApplication) {
sDefaultSmsApplication = defaultSmsApplication;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2020 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.car.settings.testutils;
import android.telephony.SmsManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(SmsManager.class)
public class ShadowSmsManager {
private static SmsManager sSmsManager;
public static void setDefault(SmsManager smsManager) {
sSmsManager = smsManager;
}
@Resetter
public static void reset() {
sSmsManager = null;
}
@Implementation
public static SmsManager getDefault() {
return sSmsManager;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.os.storage.VolumeInfo;
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(StorageManagerVolumeProvider.class)
public class ShadowStorageManagerVolumeProvider {
private static VolumeInfo sVolumeInfo;
@Resetter
public static void reset() {
sVolumeInfo = null;
}
@Implementation
protected VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume) {
return sVolumeInfo;
}
public static void setVolumeInfo(VolumeInfo volumeInfo) {
sVolumeInfo = volumeInfo;
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.SubscriptionPlan;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.ArrayList;
import java.util.List;
@Implements(SubscriptionManager.class)
public class ShadowSubscriptionManager extends org.robolectric.shadows.ShadowSubscriptionManager {
private static SubscriptionInfo sDefaultDataSubscriptionInfo = null;
private List<SubscriptionPlan> mSubscriptionPlanList;
private List<SubscriptionInfo> mSelectableSubscriptionInfoList;
private List<OnSubscriptionsChangedListener> mOnSubscriptionsChangedListeners =
new ArrayList<>();
private int mCurrentActiveSubscriptionId;
@Implementation
protected List<SubscriptionPlan> getSubscriptionPlans(int subId) {
return mSubscriptionPlanList;
}
public void setSubscriptionPlans(List<SubscriptionPlan> subscriptionPlanList) {
mSubscriptionPlanList = subscriptionPlanList;
}
@Implementation
protected SubscriptionInfo getDefaultDataSubscriptionInfo() {
return sDefaultDataSubscriptionInfo;
}
public static void setDefaultDataSubscriptionInfo(SubscriptionInfo subscriptionInfo) {
sDefaultDataSubscriptionInfo = subscriptionInfo;
}
@Implementation
protected List<SubscriptionInfo> getSelectableSubscriptionInfoList() {
return mSelectableSubscriptionInfoList;
}
public void setSelectableSubscriptionInfoList(List<SubscriptionInfo> infos) {
mSelectableSubscriptionInfoList = infos;
}
@Implementation
protected void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
super.addOnSubscriptionsChangedListener(listener);
mOnSubscriptionsChangedListeners.add(listener);
}
@Implementation
protected void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
super.removeOnSubscriptionsChangedListener(listener);
mOnSubscriptionsChangedListeners.remove(listener);
}
public List<OnSubscriptionsChangedListener> getOnSubscriptionChangedListeners() {
return mOnSubscriptionsChangedListeners;
}
@Implementation
protected boolean isActiveSubscriptionId(int subscriptionId) {
return mCurrentActiveSubscriptionId == subscriptionId;
}
public void setCurrentActiveSubscriptionId(int subscriptionId) {
mCurrentActiveSubscriptionId = subscriptionId;
}
@Resetter
public static void reset() {
org.robolectric.shadows.ShadowSubscriptionManager.reset();
sDefaultDataSubscriptionInfo = null;
}
}

View File

@@ -0,0 +1,115 @@
/*
* 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.car.settings.testutils;
import static android.telephony.PhoneStateListener.LISTEN_NONE;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Implements(TelephonyManager.class)
public class ShadowTelephonyManager extends org.robolectric.shadows.ShadowTelephonyManager {
public static final String SUBSCRIBER_ID = "test_id";
private static Map<Integer, Integer> sSubIdsWithResetCalledCount = new HashMap<>();
private static int sSimCount = 1;
private final Map<PhoneStateListener, Integer> mPhoneStateRegistrations = new HashMap<>();
private boolean mIsDataEnabled = false;
private boolean mIsRoamingEnabled = false;
public static boolean verifyFactoryResetCalled(int subId, int numTimes) {
if (!sSubIdsWithResetCalledCount.containsKey(subId)) return false;
return sSubIdsWithResetCalledCount.get(subId) == numTimes;
}
@Implementation
protected void listen(PhoneStateListener listener, int flags) {
super.listen(listener, flags);
if (flags == LISTEN_NONE) {
mPhoneStateRegistrations.remove(listener);
} else {
mPhoneStateRegistrations.put(listener, flags);
}
}
public List<PhoneStateListener> getListenersForFlags(int flags) {
List<PhoneStateListener> listeners = new ArrayList<>();
for (PhoneStateListener listener : mPhoneStateRegistrations.keySet()) {
if ((mPhoneStateRegistrations.get(listener) & flags) != 0) {
listeners.add(listener);
}
}
return listeners;
}
@Implementation
public void setDataEnabled(boolean enable) {
mIsDataEnabled = enable;
}
@Implementation
public boolean isDataEnabled() {
return mIsDataEnabled;
}
@Implementation
protected void factoryReset(int subId) {
sSubIdsWithResetCalledCount.put(subId,
sSubIdsWithResetCalledCount.getOrDefault(subId, 0) + 1);
}
@Implementation
protected String getSubscriberId(int subId) {
return subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID ? null : SUBSCRIBER_ID;
}
@Implementation
protected int getSimCount() {
return sSimCount;
}
public static void setSimCount(int simCount) {
sSimCount = simCount;
}
@Implementation
protected void setDataRoamingEnabled(boolean isEnabled) {
mIsRoamingEnabled = isEnabled;
}
@Implementation
protected boolean isDataRoamingEnabled() {
return mIsRoamingEnabled;
}
@Resetter
public static void reset() {
sSubIdsWithResetCalledCount.clear();
sSimCount = 1;
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.content.Context;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.Voice;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.Locale;
@Implements(TextToSpeech.class)
public class ShadowTextToSpeech {
private static TextToSpeech sInstance;
private static TextToSpeech.OnInitListener sOnInitListener;
private static String sEngine;
public static void setInstance(TextToSpeech textToSpeech) {
sInstance = textToSpeech;
}
/**
* Override constructor and only store the name of the last constructed engine and init
* listener.
*/
public void __constructor__(Context context, TextToSpeech.OnInitListener listener,
String engine,
String packageName, boolean useFallback) {
sOnInitListener = listener;
sEngine = engine;
}
public void __constructor__(Context context, TextToSpeech.OnInitListener listener,
String engine) {
__constructor__(context, listener, engine, null, false);
}
public void __constructor__(Context context, TextToSpeech.OnInitListener listener) {
__constructor__(context, listener, null, null, false);
}
@Implementation
protected String getCurrentEngine() {
return sInstance.getCurrentEngine();
}
@Implementation
protected int setLanguage(final Locale loc) {
return sInstance.setLanguage(loc);
}
@Implementation
protected void shutdown() {
sInstance.shutdown();
}
@Implementation
protected int setSpeechRate(float speechRate) {
return sInstance.setSpeechRate(speechRate);
}
@Implementation
protected int setPitch(float pitch) {
return sInstance.setPitch(pitch);
}
@Implementation
protected Voice getVoice() {
return sInstance.getVoice();
}
@Implementation
protected int isLanguageAvailable(final Locale loc) {
return sInstance.isLanguageAvailable(loc);
}
@Implementation
protected int speak(final CharSequence text,
final int queueMode,
final Bundle params,
final String utteranceId) {
return sInstance.speak(text, queueMode, params, utteranceId);
}
@Resetter
public static void reset() {
sInstance = null;
sOnInitListener = null;
sEngine = null;
}
/** Check for the last constructed engine name. */
public static String getLastConstructedEngine() {
return sEngine;
}
/** Trigger the initializtion callback given the input status. */
public static void callInitializationCallbackWithStatus(int status) {
sOnInitListener.onInit(status);
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.content.Intent;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TtsEngines;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.List;
import java.util.Locale;
@Implements(TtsEngines.class)
public class ShadowTtsEngines {
private static TtsEngines sInstance;
public static void setInstance(TtsEngines ttsEngines) {
sInstance = ttsEngines;
}
@Resetter
public static void reset() {
sInstance = null;
}
@Implementation
protected List<TextToSpeech.EngineInfo> getEngines() {
return sInstance.getEngines();
}
@Implementation
protected TextToSpeech.EngineInfo getEngineInfo(String packageName) {
return sInstance.getEngineInfo(packageName);
}
@Implementation
protected String getDefaultEngine() {
return sInstance.getDefaultEngine();
}
@Implementation
protected Intent getSettingsIntent(String engine) {
return sInstance.getSettingsIntent(engine);
}
@Implementation
protected boolean isLocaleSetToDefaultForEngine(String engineName) {
return sInstance.isLocaleSetToDefaultForEngine(engineName);
}
@Implementation
protected Locale getLocalePrefForEngine(String engineName) {
return sInstance.getLocalePrefForEngine(engineName);
}
@Implementation
protected synchronized void updateLocalePrefForEngine(String engineName, Locale newLocale) {
sInstance.updateLocalePrefForEngine(engineName, newLocale);
}
@Implementation
protected Locale parseLocaleString(String localeString) {
return sInstance.parseLocaleString(localeString);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.graphics.Typeface;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
/**
* This is to shadow an extra function that old versions of Robolectric don't cover.
*/
@Implements(value = Typeface.class)
public class ShadowTypeface extends org.robolectric.shadows.ShadowLegacyTypeface {
@Implementation
public static Typeface create(Typeface family, int weight, boolean italic) {
return create(family, 0);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2011 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.car.settings.testutils;
import com.android.settingslib.net.UidDetail;
import com.android.settingslib.net.UidDetailProvider;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(UidDetailProvider.class)
public class ShadowUidDetailProvider {
private static UidDetail sUidDetail;
@Resetter
public static void reset() {
sUidDetail = null;
}
@Implementation
public UidDetail getUidDetail(int uid, boolean blocking) {
return sUidDetail;
}
public static void setUidDetail(UidDetail uidDetail) {
sUidDetail = uidDetail;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.UserInfo;
import com.android.car.settings.profiles.ProfileHelper;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.List;
import java.util.function.Predicate;
/**
* Shadow for {@link ProfileHelper}.
*/
@Implements(ProfileHelper.class)
public class ShadowUserHelper {
private static ProfileHelper sInstance;
public static void setInstance(ProfileHelper profileHelper) {
sInstance = profileHelper;
}
@Resetter
public static void reset() {
sInstance = null;
}
@Implementation
protected boolean canCurrentProcessModifyAccounts() {
return sInstance.canCurrentProcessModifyAccounts();
}
@Implementation
protected List<UserInfo> getAllUsers() {
return sInstance.getAllProfiles();
}
@Implementation
protected List<UserInfo> getAllSwitchableUsers() {
return sInstance.getAllSwitchableProfiles();
}
@Implementation
protected List<UserInfo> getAllPersistentUsers() {
return sInstance.getAllPersistentProfiles();
}
@Implementation
public boolean isCurrentProcessUser(UserInfo userInfo) {
return sInstance.isCurrentProcessUser(userInfo);
}
@Implementation
protected UserInfo getCurrentProcessUserInfo() {
return sInstance.getCurrentProcessUserInfo();
}
@Implementation
@ProfileHelper.RemoveProfileResult
public int removeUser(Context context, UserInfo userInfo) {
return sInstance.removeProfile(context, userInfo);
}
@Implementation
public UserInfo createNewOrFindExistingGuest(Context context) {
return sInstance.createNewOrFindExistingGuest(context);
}
@Implementation
public List<UserInfo> getAllLivingUsers(@Nullable Predicate<? super UserInfo> filter) {
return sInstance.getAllLivingProfiles(filter);
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.car.settings.testutils;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.UserManager;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import com.android.car.settings.profiles.ProfileIconProvider;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(ProfileIconProvider.class)
public class ShadowUserIconProvider {
@Implementation
protected RoundedBitmapDrawable getRoundedUserIcon(UserInfo userInfo, Context context) {
return null;
}
@Implementation
protected RoundedBitmapDrawable getRoundedGuestDefaultIcon(Resources resources) {
return null;
}
@Implementation
protected Bitmap assignDefaultIcon(
UserManager userManager, Resources resources, UserInfo userInfo) {
return null;
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.car.settings.testutils;
import android.annotation.UserIdInt;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Implements(UserManager.class)
public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
private static boolean sIsHeadlessSystemUserMode = true;
private static boolean sCanAddMoreUsers = true;
private static Map<Integer, List<UserInfo>> sProfiles = new ArrayMap<>();
private static Map<Integer, Bitmap> sUserIcons = new ArrayMap<>();
@Implementation
protected int[] getProfileIdsWithDisabled(int userId) {
if (sProfiles.containsKey(userId)) {
return sProfiles.get(userId).stream().mapToInt(userInfo -> userInfo.id).toArray();
}
return new int[]{};
}
@Implementation
protected List<UserInfo> getProfiles(int userHandle) {
if (sProfiles.containsKey(userHandle)) {
return new ArrayList<>(sProfiles.get(userHandle));
}
return Collections.emptyList();
}
/** Adds a profile to be returned by {@link #getProfiles(int)}. **/
public void addProfile(
int userHandle, int profileUserHandle, String profileName, int profileFlags) {
sProfiles.putIfAbsent(userHandle, new ArrayList<>());
sProfiles.get(userHandle).add(new UserInfo(profileUserHandle, profileName, profileFlags));
}
@Implementation
protected void setUserRestriction(String key, boolean value, UserHandle userHandle) {
setUserRestriction(userHandle, key, value);
}
@Implementation
protected List<UserInfo> getUsers(boolean excludeDying) {
return super.getUsers();
}
@Implementation
protected static boolean isHeadlessSystemUserMode() {
return sIsHeadlessSystemUserMode;
}
public static void setIsHeadlessSystemUserMode(boolean isEnabled) {
sIsHeadlessSystemUserMode = isEnabled;
}
@Implementation
protected static boolean canAddMoreUsers() {
return sCanAddMoreUsers;
}
public static void setCanAddMoreUsers(boolean isEnabled) {
sCanAddMoreUsers = isEnabled;
}
@Implementation
public Bitmap getUserIcon(@UserIdInt int userId) {
return sUserIcons.get(userId);
}
public static void setUserIcon(@UserIdInt int userId, Bitmap icon) {
sUserIcons.put(userId, icon);
}
@Resetter
public static void reset() {
org.robolectric.shadows.ShadowUserManager.reset();
sIsHeadlessSystemUserMode = true;
sCanAddMoreUsers = true;
sProfiles.clear();
sUserIcons.clear();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import com.android.settingslib.Utils;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(value = Utils.class)
public class ShadowUtils {
private static String sDeviceProvisioningPackage;
@Resetter
public static void reset() {
sDeviceProvisioningPackage = null;
}
@Implementation
protected static boolean isDeviceProvisioningPackage(Resources resources,
String packageName) {
return sDeviceProvisioningPackage != null && sDeviceProvisioningPackage.equals(
packageName);
}
@Implementation
protected static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) {
return new ColorDrawable(0);
}
public static void setDeviceProvisioningPackage(String deviceProvisioningPackage) {
sDeviceProvisioningPackage = deviceProvisioningPackage;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.service.voice.VoiceInteractionServiceInfo;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
import java.util.Map;
@Implements(VoiceInteractionServiceInfo.class)
public class ShadowVoiceInteractionServiceInfo {
private static Map<ServiceInfo, Boolean> sSupportsAssistMap = new HashMap<>();
private static Map<ServiceInfo, String> sRecognitionServiceMap = new HashMap<>();
private static Map<ServiceInfo, String> sSettingsActivityMap = new HashMap<>();
private ServiceInfo mServiceInfo;
public void __constructor__(PackageManager pm, ServiceInfo si) {
mServiceInfo = si;
}
public static void setSupportsAssist(ServiceInfo si, boolean supports) {
sSupportsAssistMap.put(si, supports);
}
public static void setRecognitionService(ServiceInfo si, String recognitionService) {
sRecognitionServiceMap.put(si, recognitionService);
}
public static void setSettingsActivity(ServiceInfo si, String settingsActivity) {
sSettingsActivityMap.put(si, settingsActivity);
}
@Implementation
protected boolean getSupportsAssist() {
return sSupportsAssistMap.get(mServiceInfo);
}
@Implementation
protected String getRecognitionService() {
return sRecognitionServiceMap.get(mServiceInfo);
}
@Implementation
protected String getSettingsActivity() {
return sSettingsActivityMap.get(mServiceInfo);
}
@Implementation
protected ServiceInfo getServiceInfo() {
return mServiceInfo;
}
@Resetter
public static void reset() {
sSupportsAssistMap.clear();
sRecognitionServiceMap.clear();
sSettingsActivityMap.clear();
}
}

View File

@@ -0,0 +1,186 @@
/*
* 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.car.settings.testutils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DhcpInfo;
import android.net.NetworkInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.util.Pair;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowConnectivityManager;
import org.robolectric.shadows.ShadowNetworkInfo;
import org.robolectric.shadows.ShadowWifiInfo;
import java.util.LinkedHashMap;
import java.util.Map;
@Implements(WifiManager.class)
public class ShadowWifiManager extends org.robolectric.shadows.ShadowWifiManager {
private static final int LOCAL_HOST = 2130706433;
private static int sResetCalledCount = 0;
private final Map<Integer, WifiConfiguration> mNetworkIdToConfiguredNetworks =
new LinkedHashMap<>();
private Pair<Integer, Boolean> mLastEnabledNetwork;
private int mLastForgottenNetwork = Integer.MIN_VALUE;
private WifiInfo mActiveWifiInfo;
@Implementation
@Override
protected int addNetwork(WifiConfiguration config) {
int networkId = mNetworkIdToConfiguredNetworks.size();
config.networkId = -1;
mNetworkIdToConfiguredNetworks.put(networkId, makeCopy(config, networkId));
return networkId;
}
@Implementation
@Override
protected void connect(WifiConfiguration wifiConfiguration,
WifiManager.ActionListener listener) {
int networkId = mNetworkIdToConfiguredNetworks.size();
mNetworkIdToConfiguredNetworks.put(networkId,
makeCopy(wifiConfiguration, wifiConfiguration.networkId));
mLastEnabledNetwork = new Pair<>(wifiConfiguration.networkId, true);
WifiInfo wifiInfo = getConnectionInfo();
String ssid = isQuoted(wifiConfiguration.SSID)
? stripQuotes(wifiConfiguration.SSID)
: wifiConfiguration.SSID;
ShadowWifiInfo shadowWifiInfo = Shadow.extract(wifiInfo);
shadowWifiInfo.setSSID(ssid);
shadowWifiInfo.setBSSID(wifiConfiguration.BSSID);
shadowWifiInfo.setNetworkId(wifiConfiguration.networkId);
setConnectionInfo(wifiInfo);
// Now that we're "connected" to wifi, update Dhcp and point it to localhost.
DhcpInfo dhcpInfo = new DhcpInfo();
dhcpInfo.gateway = LOCAL_HOST;
dhcpInfo.ipAddress = LOCAL_HOST;
setDhcpInfo(dhcpInfo);
// Now add the network to ConnectivityManager.
NetworkInfo networkInfo =
ShadowNetworkInfo.newInstance(
NetworkInfo.DetailedState.CONNECTED,
ConnectivityManager.TYPE_WIFI,
0 /* subType */,
true /* isAvailable */,
true /* isConnected */);
ShadowConnectivityManager connectivityManager =
Shadow.extract(RuntimeEnvironment.application
.getSystemService(Context.CONNECTIVITY_SERVICE));
connectivityManager.setActiveNetworkInfo(networkInfo);
mActiveWifiInfo = wifiInfo;
if (listener != null) {
listener.onSuccess();
}
}
private static boolean isQuoted(String str) {
if (str == null || str.length() < 2) {
return false;
}
return str.charAt(0) == '"' && str.charAt(str.length() - 1) == '"';
}
private static String stripQuotes(String str) {
return str.substring(1, str.length() - 1);
}
@Implementation
@Override
protected boolean reconnect() {
WifiConfiguration wifiConfiguration = getMostRecentNetwork();
if (wifiConfiguration == null) {
return false;
}
connect(wifiConfiguration, null);
return true;
}
private WifiConfiguration getMostRecentNetwork() {
if (getLastEnabledNetwork() == null) {
return null;
}
return getWifiConfiguration(getLastEnabledNetwork().first);
}
public WifiConfiguration getLastAddedNetworkConfiguration() {
return mNetworkIdToConfiguredNetworks.get(getLastAddedNetworkId());
}
public int getLastAddedNetworkId() {
return mNetworkIdToConfiguredNetworks.size() - 1;
}
@Override
public Pair<Integer, Boolean> getLastEnabledNetwork() {
return mLastEnabledNetwork;
}
public WifiInfo getActiveWifiInfo() {
return mActiveWifiInfo;
}
public static boolean verifyFactoryResetCalled(int numTimes) {
return sResetCalledCount == numTimes;
}
@Implementation
protected void forget(int netId, WifiManager.ActionListener listener) {
mLastForgottenNetwork = netId;
}
public int getLastForgottenNetwork() {
return mLastForgottenNetwork;
}
@Implementation
protected void factoryReset() {
sResetCalledCount++;
}
@Resetter
public static void reset() {
sResetCalledCount = 0;
}
private WifiConfiguration makeCopy(WifiConfiguration config, int networkId) {
WifiConfiguration copy = Shadows.shadowOf(config).copy();
copy.networkId = networkId;
return copy;
}
}