Implment get/query APIs for properties

Bug: 169258655
Test: atest CtsContentTestCases:PackageManagerGetPropertyTest
Test: atest CtsContentTestCases:PackageManagerQueryPropertyTest
Change-Id: I91d58cd42e7f1f10b0e494ff9f318a9e3fa073db
This commit is contained in:
Todd Kennedy
2020-11-18 09:27:18 -08:00
parent c0b203cc38
commit 569435e0eb
7 changed files with 590 additions and 19 deletions

View File

@@ -12105,6 +12105,8 @@ package android.content.pm {
method public abstract android.content.pm.PermissionInfo getPermissionInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public abstract int getPreferredActivities(@NonNull java.util.List<android.content.IntentFilter>, @NonNull java.util.List<android.content.ComponentName>, @Nullable String);
method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForActivity(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -12137,6 +12139,8 @@ package android.content.pm {
method public boolean isPackageSuspended();
method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String);
method public abstract boolean isSafeMode();
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryActivityProperty(@NonNull String);
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryApplicationProperty(@NonNull String);
method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int);
method @NonNull public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, int);
method @NonNull public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(@NonNull String, int);
@@ -12145,6 +12149,9 @@ package android.content.pm {
method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, int);
method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, int);
method @NonNull public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryProviderProperty(@NonNull String);
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryReceiverProperty(@NonNull String);
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryServiceProperty(@NonNull String);
method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
method public abstract void removePermission(@NonNull String);
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);

View File

@@ -58,6 +58,8 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.Property;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -3551,4 +3553,112 @@ public class ApplicationPackageManager extends PackageManager {
throw e.rethrowAsRuntimeException();
}
}
@Override
public Property getProperty(String propertyName, String packageName)
throws NameNotFoundException {
Objects.requireNonNull(packageName);
Objects.requireNonNull(propertyName);
try {
final Property property = mPM.getProperty(propertyName, packageName, null);
if (property == null) {
throw new NameNotFoundException();
}
return property;
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
public Property getProperty(String propertyName, ComponentName component)
throws NameNotFoundException {
Objects.requireNonNull(component);
Objects.requireNonNull(propertyName);
try {
final Property property = mPM.getProperty(
propertyName, component.getPackageName(), component.getClassName());
if (property == null) {
throw new NameNotFoundException();
}
return property;
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
public List<Property> queryApplicationProperty(String propertyName) {
Objects.requireNonNull(propertyName);
try {
final ParceledListSlice<Property> parceledList =
mPM.queryProperty(propertyName, TYPE_APPLICATION);
if (parceledList == null) {
return Collections.emptyList();
}
return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
public List<Property> queryActivityProperty(String propertyName) {
Objects.requireNonNull(propertyName);
try {
final ParceledListSlice<Property> parceledList =
mPM.queryProperty(propertyName, TYPE_ACTIVITY);
if (parceledList == null) {
return Collections.emptyList();
}
return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
public List<Property> queryProviderProperty(String propertyName) {
Objects.requireNonNull(propertyName);
try {
final ParceledListSlice<Property> parceledList =
mPM.queryProperty(propertyName, TYPE_PROVIDER);
if (parceledList == null) {
return Collections.emptyList();
}
return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
public List<Property> queryReceiverProperty(String propertyName) {
Objects.requireNonNull(propertyName);
try {
final ParceledListSlice<Property> parceledList =
mPM.queryProperty(propertyName, TYPE_RECEIVER);
if (parceledList == null) {
return Collections.emptyList();
}
return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
public List<Property> queryServiceProperty(String propertyName) {
Objects.requireNonNull(propertyName);
try {
final ParceledListSlice<Property> parceledList =
mPM.queryProperty(propertyName, TYPE_SERVICE);
if (parceledList == null) {
return Collections.emptyList();
}
return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
}

View File

@@ -38,6 +38,7 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.PermissionGroupInfo;
@@ -797,4 +798,7 @@ interface IPackageManager {
IBinder getHoldLockToken();
void holdLock(in IBinder token, in int durationMs);
PackageManager.Property getProperty(String propertyName, String packageName, String className);
ParceledListSlice queryProperty(String propertyName, int componentType);
}

View File

@@ -0,0 +1,20 @@
/*
**
** Copyright 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 android.content.pm;
parcelable PackageManager.Property;

View File

@@ -311,6 +311,8 @@ public abstract class PackageManager {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mName);
dest.writeInt(mType);
dest.writeString(mPackageName);
dest.writeString(mClassName);
if (mType == TYPE_BOOLEAN) {
dest.writeBoolean(mBooleanValue);
} else if (mType == TYPE_FLOAT) {
@@ -322,8 +324,6 @@ public abstract class PackageManager {
} else if (mType == TYPE_STRING) {
dest.writeString(mStringValue);
}
dest.writeString(mPackageName);
dest.writeString(mClassName);
}
@NonNull
@@ -370,6 +370,41 @@ public abstract class PackageManager {
public void onPermissionsChanged(int uid);
}
/** @hide */
public static final int TYPE_UNKNOWN = 0;
/** @hide */
public static final int TYPE_ACTIVITY = 1;
/** @hide */
public static final int TYPE_RECEIVER = 2;
/** @hide */
public static final int TYPE_SERVICE = 3;
/** @hide */
public static final int TYPE_PROVIDER = 4;
/** @hide */
public static final int TYPE_APPLICATION = 5;
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_UNKNOWN,
TYPE_ACTIVITY,
TYPE_RECEIVER,
TYPE_SERVICE,
TYPE_PROVIDER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComponentType {}
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_UNKNOWN,
TYPE_ACTIVITY,
TYPE_RECEIVER,
TYPE_SERVICE,
TYPE_PROVIDER,
TYPE_APPLICATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PropertyLocation {}
/**
* As a guiding principle:
* <p>
@@ -8601,6 +8636,88 @@ public abstract class PackageManager {
throw new UnsupportedOperationException(
"getMimeGroup not implemented in subclass");
}
/**
* Returns the property defined in the given package's &lt;appliction&gt; tag.
*
* @throws NameNotFoundException if either the given package is not installed or if the
* given property is not defined within the &lt;application&gt; tag.
*/
@NonNull
public Property getProperty(@NonNull String propertyName, @NonNull String packageName)
throws NameNotFoundException {
throw new UnsupportedOperationException(
"getProperty not implemented in subclass");
}
/**
* Returns the property defined in the given component declaration.
*
* @throws NameNotFoundException if either the given component does not exist or if the
* given property is not defined within the component declaration.
*/
@NonNull
public Property getProperty(@NonNull String propertyName, @NonNull ComponentName component)
throws NameNotFoundException {
throw new UnsupportedOperationException(
"getProperty not implemented in subclass");
}
/**
* Returns the property definition for all &lt;application&gt; tags.
* <p>If the property is not defined with any &lt;application&gt; tag,
* returns and empty list.
*/
@NonNull
public List<Property> queryApplicationProperty(@NonNull String propertyName) {
throw new UnsupportedOperationException(
"qeuryApplicationProperty not implemented in subclass");
}
/**
* Returns the property definition for all &lt;activity&gt; and &lt;activity-alias&gt; tags.
* <p>If the property is not defined with any &lt;activity&gt; and &lt;activity-alias&gt; tag,
* returns and empty list.
*/
@NonNull
public List<Property> queryActivityProperty(@NonNull String propertyName) {
throw new UnsupportedOperationException(
"qeuryActivityProperty not implemented in subclass");
}
/**
* Returns the property definition for all &lt;provider&gt; tags.
* <p>If the property is not defined with any &lt;provider&gt; tag,
* returns and empty list.
*/
@NonNull
public List<Property> queryProviderProperty(@NonNull String propertyName) {
throw new UnsupportedOperationException(
"qeuryProviderProperty not implemented in subclass");
}
/**
* Returns the property definition for all &lt;receiver&gt; tags.
* <p>If the property is not defined with any &lt;receiver&gt; tag,
* returns and empty list.
*/
@NonNull
public List<Property> queryReceiverProperty(@NonNull String propertyName) {
throw new UnsupportedOperationException(
"qeuryReceiverProperty not implemented in subclass");
}
/**
* Returns the property definition for all &lt;service&gt; tags.
* <p>If the property is not defined with any &lt;service&gt; tag,
* returns and empty list.
*/
@NonNull
public List<Property> queryServiceProperty(@NonNull String propertyName) {
throw new UnsupportedOperationException(
"qeuryServiceProperty not implemented in subclass");
}
/**
* Grants implicit visibility of the package that provides an authority to a querying UID.
*

View File

@@ -94,6 +94,11 @@ import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.RESTRICTION_NONE;
import static android.content.pm.PackageManager.TYPE_ACTIVITY;
import static android.content.pm.PackageManager.TYPE_PROVIDER;
import static android.content.pm.PackageManager.TYPE_RECEIVER;
import static android.content.pm.PackageManager.TYPE_SERVICE;
import static android.content.pm.PackageManager.TYPE_UNKNOWN;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
@@ -195,8 +200,11 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentType;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageManager.ModuleInfoFlags;
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageManager.PropertyLocation;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
@@ -339,7 +347,6 @@ import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -394,7 +401,6 @@ import libcore.util.HexEncoding;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -572,21 +578,6 @@ public class PackageManagerService extends IPackageManager.Stub
private static final int[] EMPTY_INT_ARRAY = new int[0];
private static final int TYPE_UNKNOWN = 0;
private static final int TYPE_ACTIVITY = 1;
private static final int TYPE_RECEIVER = 2;
private static final int TYPE_SERVICE = 3;
private static final int TYPE_PROVIDER = 4;
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_UNKNOWN,
TYPE_ACTIVITY,
TYPE_RECEIVER,
TYPE_SERVICE,
TYPE_PROVIDER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComponentType {}
/**
* Timeout (in milliseconds) after which the watchdog should declare that
* our handler thread is wedged. The usual default for such things is one
@@ -1317,6 +1308,8 @@ public class PackageManagerService extends IPackageManager.Stub
private final IncrementalManager mIncrementalManager;
private final PackageProperty mPackageProperty = new PackageProperty();
private static class IFVerificationParams {
String packageName;
boolean hasDomainUrls;
@@ -12708,6 +12701,37 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
@Override
public Property getProperty(String propertyName, String packageName, String className) {
Objects.requireNonNull(propertyName);
Objects.requireNonNull(packageName);
synchronized (mLock) {
final PackageSetting ps = getPackageSetting(packageName);
if (shouldFilterApplicationLocked(ps, Binder.getCallingUid(),
UserHandle.getCallingUserId())) {
return null;
}
return mPackageProperty.getProperty(propertyName, packageName, className);
}
}
@Override
public ParceledListSlice<Property> queryProperty(
String propertyName, @PropertyLocation int componentType) {
Objects.requireNonNull(propertyName);
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getCallingUserId();
final List<Property> result =
mPackageProperty.queryProperty(propertyName, componentType, packageName -> {
final PackageSetting ps = getPackageSetting(packageName);
return shouldFilterApplicationLocked(ps, callingUid, callingUserId);
});
if (result == null) {
return ParceledListSlice.emptyList();
}
return new ParceledListSlice<>(result);
}
/**
* Adds a scanned package to the system. When this method is finished, the package will
* be available for query, resolution, etc...
@@ -12834,6 +12858,7 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean isReplace =
reconciledPkg.prepareResult != null && reconciledPkg.prepareResult.replace;
mAppsFilter.addPackage(pkgSetting, isReplace);
mPackageProperty.addAllProperties(pkg);
int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
StringBuilder r = null;
@@ -12978,6 +13003,7 @@ public class PackageManagerService extends IPackageManager.Stub
private void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) {
mComponentResolver.removeAllComponents(pkg, chatty);
mPermissionManager.onPackageRemoved(pkg);
mPackageProperty.removeAllProperties(pkg);
final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations());
StringBuilder r = null;

View File

@@ -0,0 +1,287 @@
/*
* 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.server.pm;
import static android.content.pm.PackageManager.TYPE_ACTIVITY;
import static android.content.pm.PackageManager.TYPE_APPLICATION;
import static android.content.pm.PackageManager.TYPE_PROVIDER;
import static android.content.pm.PackageManager.TYPE_RECEIVER;
import static android.content.pm.PackageManager.TYPE_SERVICE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageManager.PropertyLocation;
import android.content.pm.parsing.component.ParsedComponent;
import android.os.Binder;
import android.os.UserHandle;
import android.util.ArrayMap;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
/**
* Manages properties defined within a package using the &lt;property&gt; tag.
*/
public class PackageProperty {
/**
* Mapping of property name to all defined defined properties.
* <p>This is a mapping of property name --> package map. The package
* map is a mapping of package name -> list of properties.
*/
private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mApplicationProperties;
private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mActivityProperties;
private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mProviderProperties;
private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mReceiverProperties;
private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mServiceProperties;
/**
* If the provided component is {@code null}, returns the property defined on the
* application. Otherwise, returns the property defined on the component.
*/
public Property getProperty(@NonNull String propertyName, @NonNull String packageName,
@Nullable String className) {
if (className == null) {
return getApplicationProperty(propertyName, packageName);
}
return getComponentProperty(propertyName, packageName, className);
}
/**
* Returns all properties defined at the given location.
* <p>Valid locations are {@link PackageManager#TYPE_APPLICATION},
* {@link PackageManager#TYPE_ACTIVITY}, {@link PackageManager#TYPE_PROVIDER},
* {@link PackageManager#TYPE_RECEIVER}, or {@link PackageManager#TYPE_SERVICE}.
*/
public List<Property> queryProperty(@NonNull String propertyName,
@PropertyLocation int componentType, Predicate<String> filter) {
final ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyMap;
if (componentType == TYPE_APPLICATION) {
propertyMap = mApplicationProperties;
} else if (componentType == TYPE_ACTIVITY) {
propertyMap = mActivityProperties;
} else if (componentType == TYPE_PROVIDER) {
propertyMap = mProviderProperties;
} else if (componentType == TYPE_RECEIVER) {
propertyMap = mReceiverProperties;
} else if (componentType == TYPE_SERVICE) {
propertyMap = mServiceProperties;
} else {
propertyMap = null;
}
if (propertyMap == null) {
return null;
}
final ArrayMap<String, ArrayList<Property>> packagePropertyMap =
propertyMap.get(propertyName);
if (packagePropertyMap == null) {
return null;
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getCallingUserId();
final int mapSize = packagePropertyMap.size();
final List<Property> result = new ArrayList<>(mapSize);
for (int i = 0; i < mapSize; i++) {
final String packageName = packagePropertyMap.keyAt(i);
if (filter.test(packageName)) {
continue;
}
result.addAll(packagePropertyMap.valueAt(i));
}
return result;
}
/** Adds all properties defined for the given package */
void addAllProperties(AndroidPackage pkg) {
mApplicationProperties = addProperties(pkg.getProperties(), mApplicationProperties);
mActivityProperties = addComponentProperties(pkg.getActivities(), mActivityProperties);
mProviderProperties = addComponentProperties(pkg.getProviders(), mProviderProperties);
mReceiverProperties = addComponentProperties(pkg.getReceivers(), mReceiverProperties);
mServiceProperties = addComponentProperties(pkg.getServices(), mServiceProperties);
}
/** Adds all properties defined for the given package */
void removeAllProperties(AndroidPackage pkg) {
mApplicationProperties = removeProperties(pkg.getProperties(), mApplicationProperties);
mActivityProperties = removeComponentProperties(pkg.getActivities(), mActivityProperties);
mProviderProperties = removeComponentProperties(pkg.getProviders(), mProviderProperties);
mReceiverProperties = removeComponentProperties(pkg.getReceivers(), mReceiverProperties);
mServiceProperties = removeComponentProperties(pkg.getServices(), mServiceProperties);
}
/** Add the properties defined on the given components to the property collection */
private static <T extends ParsedComponent>
ArrayMap<String, ArrayMap<String, ArrayList<Property>>> addComponentProperties(
@NonNull List<T> components,
@Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) {
ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection =
propertyCollection;
final int componentsSize = components.size();
for (int i = 0; i < componentsSize; i++) {
final Map<String, Property> properties = components.get(i).getProperties();
if (properties.size() == 0) {
continue;
}
returnCollection = addProperties(properties, returnCollection);
}
return returnCollection;
}
/** Add the given properties to the property collection */
private static ArrayMap<String, ArrayMap<String, ArrayList<Property>>> addProperties(
@NonNull Map<String, Property> properties,
@Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) {
if (properties.size() == 0) {
return propertyCollection;
}
final ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection =
propertyCollection == null ? new ArrayMap<>(10) : propertyCollection;
final Iterator<Property> iter = properties.values().iterator();
while (iter.hasNext()) {
final Property property = iter.next();
final String propertyName = property.getName();
final String packageName = property.getPackageName();
ArrayMap<String, ArrayList<Property>> propertyMap = returnCollection.get(propertyName);
if (propertyMap == null) {
propertyMap = new ArrayMap<>();
returnCollection.put(propertyName, propertyMap);
}
ArrayList<Property> packageProperties = propertyMap.get(packageName);
if (packageProperties == null) {
packageProperties = new ArrayList<>(properties.size());
propertyMap.put(packageName, packageProperties);
}
packageProperties.add(property);
}
return returnCollection;
}
/** Removes the properties defined on the given components from the property collection */
private static <T extends ParsedComponent>
ArrayMap<String, ArrayMap<String, ArrayList<Property>>> removeComponentProperties(
@NonNull List<T> components,
@Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) {
ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection =
propertyCollection;
final int componentsSize = components.size();
for (int i = 0; returnCollection != null && i < componentsSize; i++) {
final Map<String, Property> properties = components.get(i).getProperties();
if (properties.size() == 0) {
continue;
}
returnCollection = removeProperties(properties, returnCollection);
}
return returnCollection;
}
/** Removes the given properties from the property collection */
private static ArrayMap<String, ArrayMap<String, ArrayList<Property>>> removeProperties(
@NonNull Map<String, Property> properties,
@Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) {
if (propertyCollection == null) {
return null;
}
final Iterator<Property> iter = properties.values().iterator();
while (iter.hasNext()) {
final Property property = iter.next();
final String propertyName = property.getName();
final String packageName = property.getPackageName();
ArrayMap<String, ArrayList<Property>> propertyMap =
propertyCollection.get(propertyName);
if (propertyMap == null) {
// error
continue;
}
ArrayList<Property> packageProperties = propertyMap.get(packageName);
if (packageProperties == null) {
//error
continue;
}
packageProperties.remove(property);
// clean up empty structures
if (packageProperties.size() == 0) {
propertyMap.remove(packageName);
}
if (propertyMap.size() == 0) {
propertyCollection.remove(propertyName);
}
}
if (propertyCollection.size() == 0) {
return null;
}
return propertyCollection;
}
private static Property getProperty(String propertyName, String packageName, String className,
ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyMap) {
final ArrayMap<String, ArrayList<Property>> packagePropertyMap =
propertyMap.get(propertyName);
if (packagePropertyMap == null) {
return null;
}
final List<Property> propertyList = packagePropertyMap.get(packageName);
if (propertyList == null) {
return null;
}
for (int i = propertyList.size() - 1; i >= 0; i--) {
final Property property = propertyList.get(i);
if (Objects.equals(className, property.getClassName())) {
return property;
}
}
return null;
}
private Property getComponentProperty(
String propertyName, String packageName, String className) {
Property property = null;
if (property == null && mActivityProperties != null) {
property = getProperty(propertyName, packageName, className, mActivityProperties);
}
if (property == null && mProviderProperties != null) {
property = getProperty(propertyName, packageName, className, mProviderProperties);
}
if (property == null && mReceiverProperties != null) {
property = getProperty(propertyName, packageName, className, mReceiverProperties);
}
if (property == null && mServiceProperties != null) {
property = getProperty(propertyName, packageName, className, mServiceProperties);
}
return property;
}
private Property getApplicationProperty(String propertyName, String packageName) {
final ArrayMap<String, ArrayList<Property>> packagePropertyMap =
mApplicationProperties.get(propertyName);
if (packagePropertyMap == null) {
return null;
}
final List<Property> propertyList = packagePropertyMap.get(packageName);
if (propertyList == null) {
return null;
}
return propertyList.get(0);
}
}