Merge "Reorder GlobalActionsDialog.ActionDialog init logic"
diff --git a/Android.bp b/Android.bp
index 4594425..4219d36 100644
--- a/Android.bp
+++ b/Android.bp
@@ -933,6 +933,11 @@
"core/java/android/net/ipmemorystore/**/*.aidl",
],
api_dir: "aidl/networkstack",
+ backend: {
+ java: {
+ sdk_version: "28",
+ },
+ },
}
filegroup {
diff --git a/api/current.txt b/api/current.txt
index ece3536..9190a13 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6623,7 +6623,7 @@
method @Nullable public String[] getAccountTypesWithManagementDisabled();
method @Nullable public java.util.List<android.content.ComponentName> getActiveAdmins();
method @NonNull public java.util.Set<java.lang.String> getAffiliationIds(@NonNull android.content.ComponentName);
- method @Nullable public java.util.List<java.lang.String> getAlwaysOnVpnLockdownWhitelist(@NonNull android.content.ComponentName);
+ method @Nullable public java.util.Set<java.lang.String> getAlwaysOnVpnLockdownWhitelist(@NonNull android.content.ComponentName);
method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName);
method @WorkerThread @NonNull public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String);
method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName);
@@ -6736,7 +6736,7 @@
method public void setAccountManagementDisabled(@NonNull android.content.ComponentName, String, boolean);
method public void setAffiliationIds(@NonNull android.content.ComponentName, @NonNull java.util.Set<java.lang.String>);
method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean, @Nullable java.util.List<java.lang.String>) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean, @Nullable java.util.Set<java.lang.String>) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean setApplicationHidden(@NonNull android.content.ComponentName, String, boolean);
method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle);
method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -17366,15 +17366,16 @@
method @Nullable public java.util.Set<android.util.Size> getInputSizes(int);
method @NonNull public java.util.Set<java.lang.Integer> getOutputFormats();
method @IntRange(from=0) public long getOutputMinFrameDuration(int, @NonNull android.util.Size);
- method public <T> long getOutputMinFrameDuration(@NonNull Class<T>, @NonNull android.util.Size);
+ method @IntRange(from=0) public <T> long getOutputMinFrameDuration(@NonNull Class<T>, @NonNull android.util.Size);
method @Nullable public java.util.Set<android.util.Size> getOutputSizes(int);
- method public <T> java.util.Set<android.util.Size> getOutputSizes(@NonNull Class<T>);
+ method @Nullable public <T> java.util.Set<android.util.Size> getOutputSizes(@NonNull Class<T>);
method @IntRange(from=0) public long getOutputStallDuration(int, @NonNull android.util.Size);
- method public <T> long getOutputStallDuration(@NonNull Class<T>, @NonNull android.util.Size);
+ method @IntRange(from=0) public <T> long getOutputStallDuration(@NonNull Class<T>, @NonNull android.util.Size);
method public int getRecommendedUseCase();
method @Nullable public java.util.Set<java.lang.Integer> getValidOutputFormatsForInput(int);
method public boolean isOutputSupportedFor(int);
method public boolean isOutputSupportedFor(@NonNull android.view.Surface);
+ field public static final int USECASE_LOW_LATENCY_SNAPSHOT = 6; // 0x6
field public static final int USECASE_PREVIEW = 0; // 0x0
field public static final int USECASE_RAW = 5; // 0x5
field public static final int USECASE_RECORD = 1; // 0x1
@@ -22965,6 +22966,7 @@
method public void unregisterGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
method public void unregisterGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback);
method public void unregisterGnssStatusCallback(@NonNull android.location.GnssStatus.Callback);
+ field public static final String EXTRA_PROVIDER_NAME = "android.location.extra.PROVIDER_NAME";
field public static final String GPS_PROVIDER = "gps";
field public static final String KEY_LOCATION_CHANGED = "location";
field public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -39005,10 +39007,12 @@
method public static long getLong(android.content.ContentResolver, String) throws android.provider.Settings.SettingNotFoundException;
method public static String getString(android.content.ContentResolver, String);
method public static android.net.Uri getUriFor(String);
+ method @Deprecated public static boolean isLocationProviderEnabled(android.content.ContentResolver, String);
method public static boolean putFloat(android.content.ContentResolver, String, float);
method public static boolean putInt(android.content.ContentResolver, String, int);
method public static boolean putLong(android.content.ContentResolver, String, long);
method public static boolean putString(android.content.ContentResolver, String, String);
+ method @Deprecated public static void setLocationProviderEnabled(android.content.ContentResolver, String, boolean);
field public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled";
field public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
field @Deprecated public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
@@ -45244,7 +45248,7 @@
field public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
field public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
field public static final String EXTRA_HIDE_PUBLIC_SETTINGS = "android.telephony.extra.HIDE_PUBLIC_SETTINGS";
- field public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
+ field @Deprecated public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
field public static final String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH";
field public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
field public static final String EXTRA_NETWORK_COUNTRY = "android.telephony.extra.NETWORK_COUNTRY";
diff --git a/api/removed.txt b/api/removed.txt
index 5108dd0..fa07094 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -314,17 +314,11 @@
package android.net {
public class ConnectivityManager {
- method @Deprecated public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler);
method @Deprecated public boolean requestRouteToHost(int, int);
method @Deprecated public int startUsingNetworkFeature(int, String);
method @Deprecated public int stopUsingNetworkFeature(int, String);
}
- @Deprecated public abstract static class ConnectivityManager.TetheringEntitlementValueListener {
- ctor public ConnectivityManager.TetheringEntitlementValueListener();
- method public void onEntitlementResult(int);
- }
-
@Deprecated public class NetworkBadging {
method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme);
field public static final int BADGING_4K = 30; // 0x1e
@@ -553,11 +547,6 @@
field @Deprecated public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync";
}
- public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
- method @Deprecated public static boolean isLocationProviderEnabled(android.content.ContentResolver, String);
- method @Deprecated public static void setLocationProviderEnabled(android.content.ContentResolver, String, boolean);
- }
-
public static final class Settings.System extends android.provider.Settings.NameValueTable {
field public static final String APPEND_FOR_LAST_AUDIBLE = "_last_audible";
field public static final String VOLUME_ALARM = "volume_alarm";
diff --git a/api/test-current.txt b/api/test-current.txt
index 4ccfa1c..5cb918f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2548,7 +2548,8 @@
method public int getCarrierIdListVersion();
method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
- method public void setCarrierTestOverride(String, String, String, String, String, String, String);
+ method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
+ method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String);
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b8e8fdc..7a753aec 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -45,6 +45,7 @@
import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
+import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
@@ -250,6 +251,7 @@
HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true];
StyleUIChanged style_ui_changed = 179;
PrivacyIndicatorsInteracted privacy_indicators_interacted = 180;
+ AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181;
}
// Pulled events will start at field 10000.
@@ -2558,6 +2560,18 @@
}
/**
+ * Logs whenever an app is installed on external storage.
+ * Logged from:
+ frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
+ */
+message AppInstallOnExternalStorageReported {
+ // The type of external storage.
+ optional android.stats.storage.ExternalStorageType storage_type = 1;
+ // The name of the package that is installed on the sd card.
+ optional string package_name = 2;
+}
+
+/**
* Logs when an app crashes.
* Logged from:
* frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 254d7d5..c023e6f 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -319,10 +319,11 @@
return;
}
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
// Setup the bucket start time and number.
int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
- mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
+ int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
+ flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
+
mCurrentBucketNum += numBucketsForward;
VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
(long long)mCurrentBucketStartTimeNs);
@@ -375,6 +376,7 @@
// Only resets the counters, but doesn't setup the times nor numbers.
// (Do not clear since the old one is still referenced in mAnomalyTrackers).
mCurrentSlicedCounter = std::make_shared<DimToValMap>();
+ mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
}
// Rough estimate of CountMetricProducer buffer stored. This number will be
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 7dc4e2d..615c7f2 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -559,26 +559,10 @@
return;
}
VLOG("flushing...........");
- for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
- whatIt != mCurrentSlicedDurationTrackerMap.end();) {
- for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
- if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) {
- VLOG("erase bucket for key %s %s",
- whatIt->first.toString().c_str(), it->first.toString().c_str());
- it = whatIt->second.erase(it);
- } else {
- ++it;
- }
- }
- if (whatIt->second.empty()) {
- whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
- } else {
- whatIt++;
- }
- }
-
int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
- mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
+ int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
+ flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
+
mCurrentBucketNum += numBucketsForward;
}
@@ -602,6 +586,7 @@
}
}
StatsdStats::getInstance().noteBucketCount(mMetricId);
+ mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
}
void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 2b6cac8..2f9afa5 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -529,11 +529,11 @@
return;
}
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
-
// Adjusts the bucket start and end times.
int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
- mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
+ int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
+ flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
+
mCurrentBucketNum += numBucketsForward;
VLOG("Gauge metric %lld: new bucket start time: %lld", (long long)mMetricId,
(long long)mCurrentBucketStartTimeNs);
@@ -578,6 +578,7 @@
StatsdStats::getInstance().noteBucketCount(mMetricId);
mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
+ mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
}
size_t GaugeMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index a0a1348..12cec5d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -178,7 +178,7 @@
void pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition);
- void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
+ void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
ConditionState condition);
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index 52d5ffc..f7e32d4 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "ShellSubscriber.h"
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 664f0a3..f6cfe48 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -329,6 +329,9 @@
/** Returns true if the given uid is the app in the foreground. */
public abstract boolean isAppForeground(int uid);
+ /** Returns true if the given uid is currently marked 'bad' */
+ public abstract boolean isAppBad(ApplicationInfo info);
+
/** Remove pending backup for the given userId. */
public abstract void clearPendingBackup(int userId);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c0a702f..08239a1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3115,6 +3115,10 @@
if (!r.stopped) {
throw new IllegalStateException("Can't start activity that is not stopped.");
}
+ if (r.activity.mFinished) {
+ // TODO(lifecycler): How can this happen?
+ return;
+ }
// Start
activity.performStart("handleStartActivity");
@@ -3237,8 +3241,6 @@
if (!r.activity.mFinished && pendingActions != null) {
pendingActions.setOldState(r.state);
pendingActions.setRestoreInstanceState(true);
- }
- if (pendingActions != null) {
pendingActions.setCallOnPostCreate(true);
}
} else {
@@ -3940,7 +3942,7 @@
if (localLOGV) {
Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished);
}
- if (r == null) {
+ if (r == null || r.activity.mFinished) {
return null;
}
if (r.getLifecycleState() == ON_RESUME) {
@@ -4210,6 +4212,12 @@
private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
PendingTransactionActions pendingActions) {
if (r.paused) {
+ if (r.activity.mFinished) {
+ // If we are finishing, we won't call onResume() in certain cases.
+ // So here we likewise don't want to call onPause() if the activity
+ // isn't resumed.
+ return null;
+ }
RuntimeException e = new RuntimeException(
"Performing pause of activity that is not resumed: "
+ r.intent.getComponent().toShortString());
@@ -4329,13 +4337,20 @@
boolean saveState, boolean finalStateRequest, String reason) {
if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
if (r != null) {
- if (!keepShown && r.stopped && !finalStateRequest) {
- // Double stop request is possible if activity receives 'sleep' followed by 'stop'.
- final RuntimeException e = new RuntimeException(
- "Performing stop of activity that is already stopped: "
- + r.intent.getComponent().toShortString());
- Slog.e(TAG, e.getMessage(), e);
- Slog.e(TAG, r.getStateString());
+ if (!keepShown && r.stopped) {
+ if (r.activity.mFinished) {
+ // If we are finishing, we won't call onResume() in certain
+ // cases. So here we likewise don't want to call onStop()
+ // if the activity isn't resumed.
+ return;
+ }
+ if (!finalStateRequest) {
+ final RuntimeException e = new RuntimeException(
+ "Performing stop of activity that is already stopped: "
+ + r.intent.getComponent().toShortString());
+ Slog.e(TAG, e.getMessage(), e);
+ Slog.e(TAG, r.getStateString());
+ }
}
// One must first be paused before stopped...
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9a4e215..9436145 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -112,6 +112,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -5171,7 +5172,8 @@
* </ul>
* The call will fail if called with the package name of an unsupported VPN app.
* <p> Enabling lockdown via {@code lockdownEnabled} argument carries the risk that any failure
- * of the VPN provider could break networking for all apps.
+ * of the VPN provider could break networking for all apps. This method clears any lockdown
+ * whitelist set by {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)}.
*
* @param vpnPackage The package name for an installed VPN app on the device, or {@code null} to
* remove an existing always-on VPN configuration.
@@ -5181,11 +5183,11 @@
* @throws NameNotFoundException if {@code vpnPackage} is not installed.
* @throws UnsupportedOperationException if {@code vpnPackage} exists but does not support being
* set as always-on, or if always-on VPN is not available.
- * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean, List)
+ * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)
*/
public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage,
boolean lockdownEnabled) throws NameNotFoundException {
- setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled, Collections.emptyList());
+ setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled, Collections.emptySet());
}
/**
@@ -5195,6 +5197,11 @@
* System apps can always bypass VPN.
* <p> Note that the system doesn't update the whitelist when packages are installed or
* uninstalled, the admin app must call this method to keep the list up to date.
+ * <p> When {@code lockdownEnabled} is false {@code lockdownWhitelist} is ignored . When
+ * {@code lockdownEnabled} is {@code true} and {@code lockdownWhitelist} is {@code null} or
+ * empty, only system apps can bypass VPN.
+ * <p> Setting always-on VPN package to {@code null} or using
+ * {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)} clears lockdown whitelist.
*
* @param vpnPackage package name for an installed VPN app on the device, or {@code null}
* to remove an existing always-on VPN configuration
@@ -5211,13 +5218,13 @@
* available.
*/
public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage,
- boolean lockdownEnabled, @Nullable List<String> lockdownWhitelist)
+ boolean lockdownEnabled, @Nullable Set<String> lockdownWhitelist)
throws NameNotFoundException {
throwIfParentInstance("setAlwaysOnVpnPackage");
if (mService != null) {
try {
- mService.setAlwaysOnVpnPackage(
- admin, vpnPackage, lockdownEnabled, lockdownWhitelist);
+ mService.setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled,
+ lockdownWhitelist == null ? null : new ArrayList<>(lockdownWhitelist));
} catch (ServiceSpecificException e) {
switch (e.errorCode) {
case ERROR_VPN_PACKAGE_NOT_FOUND:
@@ -5255,7 +5262,7 @@
}
/**
- * Called by device or profile owner to query the list of packages that are allowed to access
+ * Called by device or profile owner to query the set of packages that are allowed to access
* the network directly when always-on VPN is in lockdown mode but not connected. Returns
* {@code null} when always-on VPN is not active or not in lockdown mode.
*
@@ -5263,13 +5270,15 @@
*
* @throws SecurityException if {@code admin} is not a device or a profile owner.
*
- * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean, List)
+ * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)
*/
- public @Nullable List<String> getAlwaysOnVpnLockdownWhitelist(@NonNull ComponentName admin) {
+ public @Nullable Set<String> getAlwaysOnVpnLockdownWhitelist(@NonNull ComponentName admin) {
throwIfParentInstance("getAlwaysOnVpnLockdownWhitelist");
if (mService != null) {
try {
- return mService.getAlwaysOnVpnLockdownWhitelist(admin);
+ final List<String> whitelist =
+ mService.getAlwaysOnVpnLockdownWhitelist(admin);
+ return whitelist == null ? null : new HashSet<>(whitelist);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index b4eaab2..d6edb90 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -22,7 +22,6 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -92,7 +91,6 @@
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @UnsupportedAppUsage
public static final String ACTION_ACTIVE_DEVICE_CHANGED =
"android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
@@ -324,7 +322,8 @@
/**
* {@inheritDoc}
*/
- @Override public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ @Override
+ public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
try {
@@ -346,7 +345,8 @@
* {@inheritDoc}
*/
@Override
- public int getConnectionState(@NonNull BluetoothDevice device) {
+ public @BluetoothProfile.BtProfileState int getConnectionState(
+ @NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
try {
mServiceLock.readLock().lock();
@@ -386,7 +386,6 @@
* @return false on immediate error, true otherwise
* @hide
*/
- @UnsupportedAppUsage
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
try {
@@ -418,7 +417,6 @@
* @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
- @UnsupportedAppUsage
public List<BluetoothDevice> getActiveDevices() {
if (VDBG) log("getActiveDevices()");
try {
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index ef77596..dabe0fd 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -276,7 +276,7 @@
* #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionState(BluetoothDevice device);
+ @BtProfileState int getConnectionState(BluetoothDevice device);
/**
* An interface for notifying BluetoothProfile IPC clients when they have
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 49a29f0a..bb1501f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4016,6 +4016,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link com.android.server.attention.AttentionManagerService} for attention services.
+ *
+ * @see #getSystemService(String)
+ * @see android.server.attention.AttentionManagerService
+ * @hide
+ */
+ public static final String ATTENTION_SERVICE = "attention";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.inputmethod.InputMethodManager} for accessing input
* methods.
*
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 2a19763..954deac 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -142,17 +142,6 @@
public static final String EXTRA_PIN_ITEM_REQUEST =
"android.content.pm.extra.PIN_ITEM_REQUEST";
- /**
- * Metadata key that specifies vouched certs, so any apps signed by a cert in vouched certs
- * will not show hidden icon in launcher even it does not have a launcher visible activity.
- *
- * If an app has this metadata in manifest, it won't be eligible to hide its icon even if its
- * cert is in vouched certs list.
- *
- * @hide
- */
- public static final String VOUCHED_CERTS_KEY = "vouched_certs";
-
private final Context mContext;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final ILauncherApps mService;
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index 6ea35f0..bae0fd3 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -315,7 +315,7 @@
/**
* The sensor is dirty. The user should be informed to clean the sensor.
*/
- public static final int SENSOR_DIRTY = 21;
+ public static final int FACE_ACQUIRED_SENSOR_DIRTY = 21;
/**
* Hardware vendors may extend this list if there are conditions that do not fall under one of
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index e751b2c..08035972 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -523,6 +523,11 @@
* cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
* CharSequence)}.
*
+ * Note: Applications generally should not cancel and start authentication in quick succession.
+ * For example, to properly handle authentication across configuration changes, it's recommended
+ * to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so, the
+ * application will not need to cancel/restart authentication during the configuration change.
+ *
* @throws IllegalArgumentException If any of the arguments are null
*
* @param crypto Object associated with the call
@@ -568,6 +573,11 @@
* authentication. The interrupted client will receive a cancelled notification through {@link
* AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
*
+ * Note: Applications generally should not cancel and start authentication in quick succession.
+ * For example, to properly handle authentication across configuration changes, it's recommended
+ * to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so, the
+ * application will not need to cancel/restart authentication during the configuration change.
+ *
* @throws IllegalArgumentException If any of the arguments are null
*
* @param cancel An object that can be used to cancel authentication
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 7ae673c..c39796b 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -336,6 +336,7 @@
* <li>{@link RecommendedStreamConfigurationMap#USECASE_SNAPSHOT}</li>
* <li>{@link RecommendedStreamConfigurationMap#USECASE_RAW}</li>
* <li>{@link RecommendedStreamConfigurationMap#USECASE_ZSL}</li>
+ * <li>{@link RecommendedStreamConfigurationMap#USECASE_LOW_LATENCY_SNAPSHOT}</li>
* </ul>
* </p>
*
@@ -400,7 +401,7 @@
public @Nullable RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
@RecommendedStreamConfigurationMap.RecommendedUsecase int usecase) {
if (((usecase >= RecommendedStreamConfigurationMap.USECASE_PREVIEW) &&
- (usecase <= RecommendedStreamConfigurationMap.USECASE_RAW)) ||
+ (usecase <= RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT)) ||
((usecase >= RecommendedStreamConfigurationMap.USECASE_VENDOR_START) &&
(usecase < RecommendedStreamConfigurationMap.MAX_USECASE_COUNT))) {
if (mRecommendedConfigurations == null) {
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 1cdf235..e909c00 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1113,8 +1113,10 @@
depthStreamDurationList.get(i), depthStreamStallList.get(i), depthScData);
}
- if ((scData.streamConfigurationArray == null) &&
- (depthScData.streamConfigurationArray == null)) {
+ if ((scData.streamConfigurationArray == null ||
+ scData.streamConfigurationArray.length == 0) &&
+ (depthScData.streamConfigurationArray == null ||
+ depthScData.streamConfigurationArray.length == 0)) {
recommendedConfigurations.add(null);
continue;
}
@@ -1125,6 +1127,7 @@
switch (i) {
case RecommendedStreamConfigurationMap.USECASE_PREVIEW:
case RecommendedStreamConfigurationMap.USECASE_RAW:
+ case RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT:
case RecommendedStreamConfigurationMap.USECASE_VIDEO_SNAPSHOT:
map = new StreamConfigurationMap(scData.streamConfigurationArray,
scData.minDurationArray, scData.stallDurationArray,
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index 068c0ce..2d72598 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
import android.graphics.PixelFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
@@ -137,6 +138,17 @@
public static final int USECASE_RAW = 0x5;
/**
+ * The recommended stream configuration map for use case low latency snapshot must contain
+ * subset of configurations with end-to-end latency that does not exceed 200 ms. under standard
+ * operating conditions (reasonable light levels, not loaded system). The expected output format
+ * will be primarily {@link android.graphics.ImageFormat#JPEG} however other image formats can
+ * be present as well. Even if available for the camera device, high speed and input
+ * configurations will be absent. This suggested configuration map may be absent on some devices
+ * that can not support any low latency requests.
+ */
+ public static final int USECASE_LOW_LATENCY_SNAPSHOT = 0x6;
+
+ /**
* Device specific use cases.
* @hide
*/
@@ -150,7 +162,8 @@
USECASE_VIDEO_SNAPSHOT,
USECASE_SNAPSHOT,
USECASE_ZSL,
- USECASE_RAW })
+ USECASE_RAW,
+ USECASE_LOW_LATENCY_SNAPSHOT})
public @interface RecommendedUsecase {};
/**
@@ -214,7 +227,7 @@
*
* @return a non-modifiable set of Integer formats
*/
- public @Nullable Set<Integer> getValidOutputFormatsForInput(int inputFormat) {
+ public @Nullable Set<Integer> getValidOutputFormatsForInput(@Format int inputFormat) {
return getUnmodifiableIntegerSet(mRecommendedMap.getValidOutputFormatsForInput(
inputFormat));
}
@@ -250,7 +263,7 @@
* @param format a format from {@link #getInputFormats}
* @return a non-modifiable set of sizes, or {@code null} if the format was not available.
*/
- public @Nullable Set<Size> getInputSizes(int format) {
+ public @Nullable Set<Size> getInputSizes(@Format int format) {
return getUnmodifiableSizeSet(mRecommendedMap.getInputSizes(format));
}
@@ -272,7 +285,7 @@
* if the image format was not a defined named constant
* from either {@link ImageFormat} or {@link PixelFormat}
*/
- public boolean isOutputSupportedFor(int format) {
+ public boolean isOutputSupportedFor(@Format int format) {
return mRecommendedMap.isOutputSupportedFor(format);
}
@@ -288,7 +301,7 @@
* @return a non-modifiable set of supported sizes,
* or {@code null} if the {@code format} is not a supported output
*/
- public @Nullable Set<Size> getOutputSizes(int format) {
+ public @Nullable Set<Size> getOutputSizes(@Format int format) {
return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(format));
}
@@ -372,7 +385,7 @@
* @return a non-modifiable set of supported slower high-resolution sizes, or {@code null} if
* the BURST_CAPTURE capability is not supported
*/
- public @Nullable Set<Size> getHighResolutionOutputSizes(int format) {
+ public @Nullable Set<Size> getHighResolutionOutputSizes(@Format int format) {
return getUnmodifiableSizeSet(mRecommendedMap.getHighResolutionOutputSizes(format));
}
@@ -392,7 +405,8 @@
*
* @throws IllegalArgumentException if {@code format} or {@code size} was not supported
*/
- public @IntRange(from = 0) long getOutputMinFrameDuration(int format, @NonNull Size size) {
+ public @IntRange(from = 0) long getOutputMinFrameDuration(@Format int format,
+ @NonNull Size size) {
return mRecommendedMap.getOutputMinFrameDuration(format, size);
}
@@ -409,7 +423,7 @@
*
* @throws IllegalArgumentException if {@code format} or {@code size} was not supported
*/
- public @IntRange(from = 0) long getOutputStallDuration(int format, @NonNull Size size) {
+ public @IntRange(from = 0) long getOutputStallDuration(@Format int format, @NonNull Size size) {
return mRecommendedMap.getOutputStallDuration(format, size);
}
@@ -425,7 +439,7 @@
* a non-modifiable set of supported sizes for {@link ImageFormat#PRIVATE} format,
* or {@code null} if the {@code klass} is not a supported output.
*/
- public <T> @Nullable Set<Size> getOutputSizes(@NonNull Class<T> klass) {
+ public @Nullable <T> Set<Size> getOutputSizes(@NonNull Class<T> klass) {
if (mSupportsPrivate) {
return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(klass));
}
@@ -448,7 +462,7 @@
*
* @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
*/
- public <T> @IntRange(from = 0) long getOutputMinFrameDuration(@NonNull final Class<T> klass,
+ public @IntRange(from = 0) <T> long getOutputMinFrameDuration(@NonNull final Class<T> klass,
@NonNull final Size size) {
if (mSupportsPrivate) {
return mRecommendedMap.getOutputMinFrameDuration(klass, size);
@@ -471,7 +485,7 @@
*
* @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
*/
- public <T> @IntRange(from = 0) long getOutputStallDuration(@NonNull final Class<T> klass,
+ public @IntRange(from = 0) <T> long getOutputStallDuration(@NonNull final Class<T> klass,
@NonNull final Size size) {
if (mSupportsPrivate) {
return mRecommendedMap.getOutputStallDuration(klass, size);
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 139a5ee..3e8c334 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -634,6 +634,8 @@
return context.getString(R.string.face_acquired_obscured);
case FACE_ACQUIRED_START:
return null;
+ case FACE_ACQUIRED_SENSOR_DIRTY:
+ return context.getString(R.string.face_acquired_sensor_dirty);
case FACE_ACQUIRED_VENDOR: {
String[] msgArray = context.getResources().getStringArray(
R.array.face_acquired_vendor);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index d08379f..e5802c2 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2816,23 +2816,6 @@
}
/**
- * @removed
- * @deprecated This API would be removed when all of caller has been updated.
- * */
- @Deprecated
- public abstract static class TetheringEntitlementValueListener {
- /**
- * Called to notify entitlement result.
- *
- * @param resultCode a int value of entitlement result. It may be one of
- * {@link #TETHER_ERROR_NO_ERROR},
- * {@link #TETHER_ERROR_PROVISION_FAILED}, or
- * {@link #TETHER_ERROR_ENTITLEMENT_UNKONWN}.
- */
- public void onEntitlementResult(int resultCode) {}
- }
-
- /**
* Get the last value of the entitlement check on this downstream. If the cached value is
* {@link #TETHER_ERROR_NO_ERROR} or showEntitlementUi argument is false, it just return the
* cached value. Otherwise, a UI-based entitlement check would be performed. It is not
@@ -2878,31 +2861,6 @@
}
/**
- * @removed
- * @deprecated This API would be removed when all of caller has been updated.
- * */
- @Deprecated
- public void getLatestTetheringEntitlementValue(int type, boolean showEntitlementUi,
- @NonNull final TetheringEntitlementValueListener listener, @Nullable Handler handler) {
- Preconditions.checkNotNull(listener, "TetheringEntitlementValueListener cannot be null.");
- ResultReceiver wrappedListener = new ResultReceiver(handler) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- listener.onEntitlementResult(resultCode);
- }
- };
-
- try {
- String pkgName = mContext.getOpPackageName();
- Log.i(TAG, "getLatestTetheringEntitlementValue:" + pkgName);
- mService.getLatestTetheringEntitlementResult(type, wrappedListener,
- showEntitlementUi, pkgName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Report network connectivity status. This is currently used only
* to alter status bar UI.
* <p>This method requires the caller to hold the permission
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 870d8b1..ea245a4 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -27,6 +27,8 @@
import android.app.Activity;
import android.app.PendingIntent;
import android.app.Service;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
@@ -48,6 +50,7 @@
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* VpnService is a base class for applications to extend and build their
@@ -138,7 +141,7 @@
* provides users with the ability to set it as always-on, so that VPN connection is
* persisted after device reboot and app upgrade. Always-on VPN can also be enabled by device
* owner and profile owner apps through
- * {@link android.app.admin.DevicePolicyManager#setAlwaysOnVpnPackage}.
+ * {@link DevicePolicyManager#setAlwaysOnVpnPackage}.
*
* <p>VPN apps not supporting this feature should opt out by adding this meta-data field to the
* {@code VpnService} component of {@code AndroidManifest.xml}. In case there is more than one
@@ -370,7 +373,10 @@
}
/**
- * Returns whether the service is running in always-on VPN mode.
+ * Returns whether the service is running in always-on VPN mode. In this mode the system ensures
+ * that the service is always running by restarting it when necessary, e.g. after reboot.
+ *
+ * @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)
*/
public final boolean isAlwaysOn() {
try {
@@ -381,8 +387,11 @@
}
/**
- * Returns whether the service is running in always-on VPN mode blocking connections without
- * VPN.
+ * Returns whether the service is running in always-on VPN lockdown mode. In this mode the
+ * system ensures that the service is always running and that the apps aren't allowed to bypass
+ * the VPN.
+ *
+ * @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)
*/
public final boolean isLockdownEnabled() {
try {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index df5da6c..313384d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9053,7 +9053,6 @@
* @return true if the provider is enabled
*
* @deprecated use {@link LocationManager#isProviderEnabled(String)}
- * @removed no longer supported
*/
@Deprecated
public static boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
@@ -9063,12 +9062,12 @@
}
/**
- * Thread-safe method for enabling or disabling a single location provider.
+ * Thread-safe method for enabling or disabling a single location provider. This will have
+ * no effect on Android Q and above.
* @param cr the content resolver to use
* @param provider the location provider to enable or disable
* @param enabled true if the provider should be enabled
* @deprecated This API is deprecated
- * @removed no longer supported
*/
@Deprecated
public static void setLocationProviderEnabled(ContentResolver cr,
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index c794a69..c1d122a 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -16,11 +16,20 @@
package android.view;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
+
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
+import android.os.SystemClock;
+import android.util.StatsLog;
/**
* Detects various gestures and events using the supplied {@link MotionEvent}s.
@@ -251,8 +260,12 @@
private boolean mAlwaysInTapRegion;
private boolean mAlwaysInBiggerTapRegion;
private boolean mIgnoreNextUpEvent;
+ // Whether a classification has been recorded by statsd for the current event stream. Reset on
+ // ACTION_DOWN.
+ private boolean mHasRecordedClassification;
private MotionEvent mCurrentDownEvent;
+ private MotionEvent mCurrentMotionEvent;
private MotionEvent mPreviousUpEvent;
/**
@@ -297,6 +310,7 @@
break;
case LONG_PRESS:
+ recordGestureClassification(msg.arg1);
dispatchLongPress();
break;
@@ -304,6 +318,8 @@
// If the user's finger is still down, do not count it as a tap
if (mDoubleTapListener != null) {
if (!mStillDown) {
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
} else {
mDeferConfirmSingleTap = true;
@@ -501,6 +517,11 @@
final int action = ev.getAction();
+ if (mCurrentMotionEvent != null) {
+ mCurrentMotionEvent.recycle();
+ }
+ mCurrentMotionEvent = MotionEvent.obtain(ev);
+
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
@@ -569,6 +590,8 @@
&& isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
// This is a second tap
mIsDoubleTapping = true;
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP);
// Give a callback with the first tap of the double-tap
handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
// Give a callback with down event of the double-tap
@@ -590,11 +613,17 @@
mStillDown = true;
mInLongPress = false;
mDeferConfirmSingleTap = false;
+ mHasRecordedClassification = false;
if (mIsLongpressEnabled) {
mHandler.removeMessages(LONG_PRESS);
- mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
- + ViewConfiguration.getLongPressTimeout());
+ mHandler.sendMessageAtTime(
+ mHandler.obtainMessage(
+ LONG_PRESS,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS,
+ 0 /* arg2 */),
+ mCurrentDownEvent.getDownTime()
+ + ViewConfiguration.getLongPressTimeout());
}
mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
@@ -613,6 +642,8 @@
final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
// Give the move events of the double-tap
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP);
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
final int deltaX = (int) (focusX - mDownFocusX);
@@ -635,8 +666,12 @@
// reschedule long press with a modified timeout.
mHandler.removeMessages(LONG_PRESS);
final long longPressTimeout = ViewConfiguration.getLongPressTimeout();
- mHandler.sendEmptyMessageAtTime(LONG_PRESS, ev.getDownTime()
- + (long) (longPressTimeout * multiplier));
+ mHandler.sendMessageAtTime(
+ mHandler.obtainMessage(
+ LONG_PRESS,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS,
+ 0 /* arg2 */),
+ ev.getDownTime() + (long) (longPressTimeout * multiplier));
}
// Inhibit default scroll. If a gesture is ambiguous, we prevent scroll
// until the gesture is resolved.
@@ -646,6 +681,8 @@
}
if (distance > slopSquare) {
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL);
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
@@ -659,6 +696,7 @@
mAlwaysInBiggerTapRegion = false;
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
+ recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL);
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
@@ -667,7 +705,11 @@
motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;
if (deepPress && hasPendingLongPress) {
mHandler.removeMessages(LONG_PRESS);
- mHandler.sendEmptyMessage(LONG_PRESS);
+ mHandler.sendMessage(
+ mHandler.obtainMessage(
+ LONG_PRESS,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS,
+ 0 /* arg2 */));
}
break;
@@ -676,11 +718,15 @@
MotionEvent currentUpEvent = MotionEvent.obtain(ev);
if (mIsDoubleTapping) {
// Finally, give the up event of the double-tap
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP);
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mInLongPress) {
mHandler.removeMessages(TAP);
mInLongPress = false;
} else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) {
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
handled = mListener.onSingleTapUp(ev);
if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
mDoubleTapListener.onSingleTapConfirmed(ev);
@@ -821,4 +867,21 @@
mInLongPress = true;
mListener.onLongPress(mCurrentDownEvent);
}
+
+ private void recordGestureClassification(int classification) {
+ if (mHasRecordedClassification
+ || classification
+ == TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION) {
+ // Only record the first classification for an event stream.
+ return;
+ }
+ StatsLog.write(
+ StatsLog.TOUCH_GESTURE_CLASSIFIED,
+ getClass().getName(),
+ classification,
+ (int) (SystemClock.uptimeMillis() - mCurrentMotionEvent.getDownTime()),
+ (float) Math.hypot(mCurrentMotionEvent.getRawX() - mCurrentDownEvent.getRawX(),
+ mCurrentMotionEvent.getRawY() - mCurrentDownEvent.getRawY()));
+ mHasRecordedClassification = true;
+ }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b857f1e..49eb78d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,6 +17,10 @@
package android.view;
import static android.content.res.Resources.ID_NULL;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
@@ -96,6 +100,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.StateSet;
+import android.util.StatsLog;
import android.util.SuperNotCalledException;
import android.util.TypedValue;
import android.view.AccessibilityIterators.CharacterTextSegmentIterator;
@@ -14542,7 +14547,12 @@
if (clickable) {
setPressed(true, x, y);
}
- checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y);
+ checkForLongClick(
+ ViewConfiguration.getLongPressTimeout(),
+ x,
+ y,
+ // This is not a touch gesture -- do not classify it as one.
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION);
return true;
}
}
@@ -15283,7 +15293,11 @@
mHasPerformedLongPress = false;
if (!clickable) {
- checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y);
+ checkForLongClick(
+ ViewConfiguration.getLongPressTimeout(),
+ x,
+ y,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
break;
}
@@ -15307,7 +15321,11 @@
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
- checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y);
+ checkForLongClick(
+ ViewConfiguration.getLongPressTimeout(),
+ x,
+ y,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
break;
@@ -15344,7 +15362,11 @@
* ambiguousMultiplier);
// Subtract the time already spent
delay -= event.getEventTime() - event.getDownTime();
- checkForLongClick(delay, x, y);
+ checkForLongClick(
+ delay,
+ x,
+ y,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
touchSlop *= ambiguousMultiplier;
}
@@ -15366,7 +15388,11 @@
if (deepPress && hasPendingLongPressCallback()) {
// process the long click action immediately
removeLongPressCallback();
- checkForLongClick(0 /* send immediately */, x, y);
+ checkForLongClick(
+ 0 /* send immediately */,
+ x,
+ y,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS);
}
break;
@@ -26031,7 +26057,7 @@
}
}
- private void checkForLongClick(long delay, float x, float y) {
+ private void checkForLongClick(long delay, float x, float y, int classification) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) {
mHasPerformedLongPress = false;
@@ -26041,6 +26067,7 @@
mPendingCheckForLongPress.setAnchor(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
mPendingCheckForLongPress.rememberPressedState();
+ mPendingCheckForLongPress.setClassification(classification);
postDelayed(mPendingCheckForLongPress, delay);
}
}
@@ -27598,11 +27625,17 @@
private float mX;
private float mY;
private boolean mOriginalPressedState;
+ /**
+ * The classification of the long click being checked: one of the
+ * StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__* constants.
+ */
+ private int mClassification;
@Override
public void run() {
if ((mOriginalPressedState == isPressed()) && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
+ recordGestureClassification(mClassification);
if (performLongClick(mX, mY)) {
mHasPerformedLongPress = true;
}
@@ -27621,6 +27654,10 @@
public void rememberPressedState() {
mOriginalPressedState = isPressed();
}
+
+ public void setClassification(int classification) {
+ mClassification = classification;
+ }
}
private final class CheckForTap implements Runnable {
@@ -27633,17 +27670,28 @@
setPressed(true, x, y);
final long delay =
ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout();
- checkForLongClick(delay, x, y);
+ checkForLongClick(delay, x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
}
private final class PerformClick implements Runnable {
@Override
public void run() {
+ recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
performClickInternal();
}
}
+ /** Records a classification for the current event stream. */
+ private void recordGestureClassification(int classification) {
+ if (classification == TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION) {
+ return;
+ }
+ // To avoid negatively impacting View performance, the latency and displacement metrics
+ // are omitted.
+ StatsLog.write(StatsLog.TOUCH_GESTURE_CLASSIFIED, getClass().getName(), classification);
+ }
+
/**
* This method returns a ViewPropertyAnimator object, which can be used to animate
* specific properties on this View.
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5f23719..07b82d0 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -122,6 +122,12 @@
private static boolean sPreloadComplete;
+ /**
+ * Cached classloader to use for the system server. Will only be populated in the system
+ * server process.
+ */
+ private static ClassLoader sCachedSystemServerClassLoader = null;
+
static void preload(TimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
bootTimingsTraceLog.traceBegin("BeginPreload");
@@ -443,7 +449,13 @@
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
if (systemServerClasspath != null) {
- performSystemServerDexOpt(systemServerClasspath);
+ if (performSystemServerDexOpt(systemServerClasspath)) {
+ // Throw away the cached classloader. If we compiled here, the classloader would
+ // not have had AoT-ed artifacts.
+ // Note: This only works in a very special environment where selinux enforcement is
+ // disabled, e.g., Mac builds.
+ sCachedSystemServerClassLoader = null;
+ }
// Capturing profiles is only supported for debug or eng builds since selinux normally
// prevents it.
boolean profileSystemServer = SystemProperties.getBoolean(
@@ -476,10 +488,9 @@
throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
} else {
- ClassLoader cl = null;
- if (systemServerClasspath != null) {
- cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);
-
+ createSystemServerClassLoader();
+ ClassLoader cl = sCachedSystemServerClassLoader;
+ if (cl != null) {
Thread.currentThread().setContextClassLoader(cl);
}
@@ -494,6 +505,24 @@
}
/**
+ * Create the classloader for the system server and store it in
+ * {@link sCachedSystemServerClassLoader}. This function may be called through JNI in
+ * system server startup, when the runtime is in a critically low state. Do not do
+ * extended computation etc here.
+ */
+ private static void createSystemServerClassLoader() {
+ if (sCachedSystemServerClassLoader != null) {
+ return;
+ }
+ final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
+ // TODO: Should we run optimization here?
+ if (systemServerClasspath != null) {
+ sCachedSystemServerClassLoader = createPathClassLoader(systemServerClasspath,
+ VMRuntime.SDK_VERSION_CUR_DEVELOPMENT);
+ }
+ }
+
+ /**
* Note that preparing the profiles for system server does not require special selinux
* permissions. From the installer perspective the system server is a regular package which can
* capture profile information.
@@ -557,15 +586,16 @@
/**
* Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction
- * set of the current runtime.
+ * set of the current runtime. If something was compiled, return true.
*/
- private static void performSystemServerDexOpt(String classPath) {
+ private static boolean performSystemServerDexOpt(String classPath) {
final String[] classPathElements = classPath.split(":");
final IInstalld installd = IInstalld.Stub
.asInterface(ServiceManager.getService("installd"));
final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
String classPathForElement = "";
+ boolean compiledSomething = false;
for (String classPathElement : classPathElements) {
// System server is fully AOTed and never profiled
// for profile guided compilation.
@@ -607,6 +637,7 @@
uuid, classLoaderContext, seInfo, false /* downgrade */,
targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
"server-dexopt");
+ compiledSomething = true;
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
@@ -617,6 +648,8 @@
classPathForElement = encodeSystemServerClassPath(
classPathForElement, classPathElement);
}
+
+ return compiledSomething;
}
/**
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 3be7c3e..41e2fc8 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -62,6 +62,7 @@
void systemReady();
void userPresent(int userId);
int getStrongAuthForUser(int userId);
+ boolean hasPendingEscrowToken(int userId);
// Keystore RecoveryController methods.
// {@code ServiceSpecificException} may be thrown to signal an error, which caller can
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index c095376..1965609 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1638,7 +1638,7 @@
}
/**
- * @see StrongAuthTracker#isFingerprintAllowedForUser
+ * @see StrongAuthTracker#isBiometricAllowedForUser(int)
*/
public boolean isBiometricAllowedForUser(int userId) {
return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_BIOMETRIC) == 0;
@@ -1980,6 +1980,18 @@
}
/**
+ * Returns whether the given user has pending escrow tokens
+ */
+ public boolean hasPendingEscrowToken(int userId) {
+ try {
+ return getLockSettings().hasPendingEscrowToken(userId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ /**
* Return true if the device supports the lock screen feature, false otherwise.
*/
public boolean hasSecureLockScreen() {
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.h b/core/jni/android/graphics/HarfBuzzNGFaceSkia.h
deleted file mode 100644
index 3308d5d..0000000
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2011, The Android Open Source Project
- * Copyright 2011, Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _ANDROID_GRAPHICS_HARF_BUZZ_NG_FACE_SKIA_H_
-#define _ANDROID_GRAPHICS_HARF_BUZZ_NG_FACE_SKIA_H_
-
-#include <SkScalar.h>
-#include <SkPaint.h>
-
-#include <hb.h>
-
-namespace android {
-
-static inline float
-HBFixedToFloat (hb_position_t v)
-{
- return scalbnf (v, -8);
-}
-
-static inline hb_position_t
-HBFloatToFixed (float v)
-{
- return scalbnf (v, +8);
-}
-
-static inline hb_position_t SkScalarToHBFixed(SkScalar value) {
- return HBFloatToFixed(SkScalarToFloat(value));
-}
-
-hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData);
-
-hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY);
-
-} // namespace android
-
-#endif // _ANDROID_GRAPHICS_HARF_BUZZ_NG_FACE_SKIA_H_
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 9f9fbf9..c4c16ee 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -31,6 +31,7 @@
#include <renderthread/CanvasContext.h>
#include <TreeInfo.h>
#include <hwui/Paint.h>
+#include <utils/TraceUtils.h>
#include "core_jni_helpers.h"
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 009a6ca..cde1884 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -24,6 +24,7 @@
#endif
#define LOG_TAG "Zygote"
+#define ATRACE_TAG ATRACE_TAG_DALVIK
#include <async_safe/log.h>
@@ -73,6 +74,7 @@
#include <cutils/multiuser.h>
#include <private/android_filesystem_config.h>
#include <utils/String8.h>
+#include <utils/Trace.h>
#include <selinux/android.h>
#include <seccomp_policy.h>
#include <stats_event_list.h>
@@ -111,11 +113,16 @@
static const char kIsolatedStorage[] = "persist.sys.isolated_storage";
static const char kIsolatedStorageSnapshot[] = "sys.isolated_storage_snapshot";
-static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
+
+static constexpr const char* kZygoteClassName = "com/android/internal/os/Zygote";
static jclass gZygoteClass;
static jmethodID gCallPostForkSystemServerHooks;
static jmethodID gCallPostForkChildHooks;
+static constexpr const char* kZygoteInitClassName = "com/android/internal/os/ZygoteInit";
+static jclass gZygoteInitClass;
+static jmethodID gCreateSystemServerClassLoader;
+
static bool g_is_security_enforced = true;
/**
@@ -585,6 +592,8 @@
}
static int UnmountTree(const char* path) {
+ ATRACE_CALL();
+
size_t path_len = strlen(path);
FILE* fp = setmntent("/proc/mounts", "r");
@@ -627,6 +636,8 @@
}
static void CreatePkgSandboxTarget(userid_t user_id, fail_fn_t fail_fn) {
+ ATRACE_CALL();
+
// Create /mnt/user/0/package
std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
CreateDir(pkg_sandbox_dir, 0751, AID_ROOT, AID_ROOT, fail_fn);
@@ -650,6 +661,8 @@
uid_t uid,
const char* dir_name,
fail_fn_t fail_fn) {
+ ATRACE_CALL();
+
std::string mnt_source_dir = StringPrintf("%s/Android/%s/%s",
mnt_source_root.c_str(), dir_name, package_name.c_str());
@@ -662,6 +675,8 @@
static void CreateSubDirs(int parent_fd, const std::string& parent_path,
const std::vector<std::string>& sub_dirs,
fail_fn_t fail_fn) {
+ ATRACE_CALL();
+
for (auto& dir_name : sub_dirs) {
struct stat sb;
if (TEMP_FAILURE_RETRY(fstatat(parent_fd, dir_name.c_str(), &sb, 0)) == 0) {
@@ -686,6 +701,8 @@
const std::vector<std::string>& package_names,
bool create_sandbox_dir,
fail_fn_t fail_fn) {
+ ATRACE_CALL();
+
std::string android_dir = StringPrintf("%s/Android", path.c_str());
android::base::unique_fd android_fd(open(android_dir.c_str(),
O_RDONLY | O_DIRECTORY | O_CLOEXEC));
@@ -729,6 +746,7 @@
}
static void CreatePkgSandboxSource(const std::string& sandbox_source, fail_fn_t fail_fn) {
+ ATRACE_CALL();
struct stat sb;
if (TEMP_FAILURE_RETRY(stat(sandbox_source.c_str(), &sb)) == 0) {
@@ -751,6 +769,8 @@
static void PreparePkgSpecificDirs(const std::vector<std::string>& package_names,
bool mount_all_obbs, const std::string& sandbox_id,
userid_t user_id, uid_t uid, fail_fn_t fail_fn) {
+ ATRACE_CALL();
+
std::unique_ptr<DIR, decltype(&closedir)> dirp(opendir("/storage"), closedir);
if (!dirp) {
fail_fn(CREATE_ERROR("Failed to opendir /storage: %s", strerror(errno)));
@@ -807,6 +827,8 @@
userid_t user_id,
const std::string& sandbox_id,
fail_fn_t fail_fn) {
+ ATRACE_CALL();
+
std::string obb_mount_dir = StringPrintf("/mnt/user/%d/obb_mount", user_id);
std::string obb_mount_file = StringPrintf("%s/%s", obb_mount_dir.c_str(), sandbox_id.c_str());
if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
@@ -844,6 +866,7 @@
const std::string& sandbox_id,
fail_fn_t fail_fn) {
// See storage config details at http://source.android.com/tech/storage/
+ ATRACE_CALL();
String8 storage_source;
if (mount_mode == MOUNT_EXTERNAL_DEFAULT) {
@@ -1420,6 +1443,15 @@
fail_fn("Error calling post fork system server hooks.");
}
+ // Prefetch the classloader for the system server. This is done early to
+ // allow a tie-down of the proper system server selinux domain.
+ env->CallStaticVoidMethod(gZygoteInitClass, gCreateSystemServerClassLoader);
+ if (env->ExceptionCheck()) {
+ // Be robust here. The Java code will attempt to create the classloader
+ // at a later point (but may not have rights to use AoT artifacts).
+ env->ExceptionClear();
+ }
+
// TODO(oth): Remove hardcoded label here (b/117874058).
static const char* kSystemServerLabel = "u:r:system_server:s0";
if (selinux_android_setcon(kSystemServerLabel) != 0) {
@@ -1986,6 +2018,13 @@
gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
"(IZZLjava/lang/String;)V");
- return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
+ gZygoteInitClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteInitClassName));
+ gCreateSystemServerClassLoader = GetStaticMethodIDOrDie(env, gZygoteInitClass,
+ "createSystemServerClassLoader",
+ "()V");
+
+ RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
+
+ return JNI_OK;
}
} // namespace android
diff --git a/core/proto/android/stats/storage/storage_enums.proto b/core/proto/android/stats/storage/storage_enums.proto
new file mode 100644
index 0000000..6892e28
--- /dev/null
+++ b/core/proto/android/stats/storage/storage_enums.proto
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package android.stats.storage;
+
+enum ExternalStorageType {
+ UNKNOWN = 0;
+ SD_CARD = 1;
+ USB = 2;
+ OTHER = 3;
+}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2b13c4e..74970e8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1538,43 +1538,45 @@
<string name="permdesc_useFaceAuthentication">Allows the app to use face authentication hardware for authentication</string>
<!-- Message shown during face acquisition when the face cannot be recognized [CHAR LIMIT=50] -->
- <string name="face_acquired_insufficient">Couldn\u2019t process face. Please try again.</string>
+ <string name="face_acquired_insufficient">Couldn\u2019t capture accurate face data. Try again.</string>
<!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] -->
- <string name="face_acquired_too_bright">Face is too bright. Please try in lower light.</string>
+ <string name="face_acquired_too_bright">Too bright. Try gentler lighting.</string>
<!-- Message shown during face acquisition when the image is too dark [CHAR LIMIT=50] -->
- <string name="face_acquired_too_dark">Face is too dark. Please uncover light source.</string>
+ <string name="face_acquired_too_dark">Too dark. Try brighter lighting.</string>
<!-- Message shown during face acquisition when the user is too close to sensor [CHAR LIMIT=50] -->
- <string name="face_acquired_too_close">Please move sensor farther from face.</string>
+ <string name="face_acquired_too_close">Move phone farther away.</string>
<!-- Message shown during face acquisition when the user is too far from sensor [CHAR LIMIT=50] -->
- <string name="face_acquired_too_far">Please bring sensor closer to face.</string>
+ <string name="face_acquired_too_far">Move phone closer.</string>
<!-- Message shown during face acquisition when the user is too high relatively to sensor [CHAR LIMIT=50] -->
- <string name="face_acquired_too_high">Please move sensor higher.</string>
+ <string name="face_acquired_too_high">Move phone higher.</string>
<!-- Message shown during face acquisition when the user is too low relatively to sensor [CHAR LIMIT=50] -->
- <string name="face_acquired_too_low">Please move sensor lower.</string>
+ <string name="face_acquired_too_low">Move phone lower.</string>
<!-- Message shown during face acquisition when the user is too right relatively to sensor [CHAR LIMIT=50] -->
- <string name="face_acquired_too_right">Please move sensor to the right.</string>
+ <string name="face_acquired_too_right">Move phone to the right.</string>
<!-- Message shown during face acquisition when the user is too left relatively to sensor [CHAR LIMIT=50] -->
- <string name="face_acquired_too_left">Please move sensor to the left.</string>
+ <string name="face_acquired_too_left">Move phone to the left.</string>
<!-- Message shown during face acquisition when the user is not front facing the sensor [CHAR LIMIT=50] -->
- <string name="face_acquired_poor_gaze">Please look at the sensor.</string>
+ <string name="face_acquired_poor_gaze">Look at the screen with your eyes open.</string>
<!-- Message shown during face acquisition when the user is not detected [CHAR LIMIT=50] -->
- <string name="face_acquired_not_detected">No face detected.</string>
+ <string name="face_acquired_not_detected">Can\u2019t see your face. Look at the phone.</string>
<!-- Message shown during face acquisition when the device is not steady [CHAR LIMIT=50] -->
- <string name="face_acquired_too_much_motion">Too much motion.</string>
+ <string name="face_acquired_too_much_motion">Too much motion. Hold phone steady.</string>
<!-- Message shown during face acquisition when the sensor needs to be recalibrated [CHAR LIMIT=50] -->
<string name="face_acquired_recalibrate">Please re-enroll your face.</string>
<!-- Message shown during face enrollment when a different person's face is detected [CHAR LIMIT=50] -->
- <string name="face_acquired_too_different">Different face detected.</string>
+ <string name="face_acquired_too_different">No longer able to recognize face. Try again.</string>
<!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] -->
<string name="face_acquired_too_similar">Too similar, please change your pose.</string>
<!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] -->
- <string name="face_acquired_pan_too_extreme">Please look more directly at the camera.</string>
+ <string name="face_acquired_pan_too_extreme">Please look more directly at the screen.</string>
<!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] -->
- <string name="face_acquired_tilt_too_extreme">Please look more directly at the camera.</string>
+ <string name="face_acquired_tilt_too_extreme">Please look more directly at the screen.</string>
<!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] -->
<string name="face_acquired_roll_too_extreme">Please straighten your head vertically.</string>
<!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] -->
- <string name="face_acquired_obscured">Please uncover your face.</string>
+ <string name="face_acquired_obscured">Clear the space between your head and the phone.</string>
+ <!-- Message shown during acquisition when the sensor is dirty [CHAR LIMIT=50] -->
+ <string name="face_acquired_sensor_dirty">Please clean the camera.</string>
<!-- Array containing custom messages shown during face acquisition from vendor. Vendor is expected to add and translate these strings -->
<string-array name="face_acquired_vendor">
</string-array>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e5fc104..17ccc31 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2546,6 +2546,7 @@
<java-symbol type="string" name="face_acquired_tilt_too_extreme" />
<java-symbol type="string" name="face_acquired_roll_too_extreme" />
<java-symbol type="string" name="face_acquired_obscured" />
+ <java-symbol type="string" name="face_acquired_sensor_dirty" />
<java-symbol type="array" name="face_acquired_vendor" />
<java-symbol type="string" name="face_name_template" />
<java-symbol type="string" name="face_authenticated_no_confirmation_required" />
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 105af6e..51c4252 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -16,7 +16,6 @@
package android.security.keystore;
-import libcore.util.EmptyArray;
import android.security.Credentials;
import android.security.GateKeeper;
import android.security.KeyStore;
@@ -31,6 +30,8 @@
import android.security.keystore.WrappedKeyEntry;
import android.util.Log;
+import libcore.util.EmptyArray;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -123,7 +124,14 @@
final Certificate[] caList;
- final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
+ // Suppress the key not found warning for this call. It seems that this error is exclusively
+ // being thrown when there is a self signed certificate chain, so when the keystore service
+ // attempts to query for the CA details, it obviously fails to find them and returns a
+ // key not found exception. This is WAI, and throwing a stack trace here can be very
+ // misleading since the trace is not clear.
+ final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias,
+ mUid,
+ true /* suppressKeyNotFoundWarning */);
if (caBytes != null) {
final Collection<X509Certificate> caChain = toCertificates(caBytes);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 4f1b2a4..ebba4cb 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -191,7 +191,7 @@
"surfacetexture/EGLConsumer.cpp",
"surfacetexture/ImageConsumer.cpp",
"surfacetexture/SurfaceTexture.cpp",
- "thread/TaskManager.cpp",
+ "thread/CommonPool.cpp",
"utils/Blur.cpp",
"utils/Color.cpp",
"utils/GLUtils.cpp",
@@ -308,6 +308,7 @@
"tests/unit/main.cpp",
"tests/unit/CacheManagerTests.cpp",
"tests/unit/CanvasContextTests.cpp",
+ "tests/unit/CommonPoolTests.cpp",
"tests/unit/DamageAccumulatorTests.cpp",
"tests/unit/DeferredLayerUpdaterTests.cpp",
"tests/unit/FatVectorTests.cpp",
@@ -381,7 +382,6 @@
"tests/microbench/LinearAllocatorBench.cpp",
"tests/microbench/PathParserBench.cpp",
"tests/microbench/RenderNodeBench.cpp",
- "tests/microbench/TaskManagerBench.cpp",
],
}
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index fe633e9..fb60a96 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -28,6 +28,7 @@
#include "hwui/Bitmap.h"
#include "utils/Color.h"
#include "utils/MathUtils.h"
+#include "utils/TraceUtils.h"
using namespace android::uirenderer::renderthread;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index a00a36f..721a115 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -25,6 +25,7 @@
#include <SkPictureRecorder.h>
#include "TreeInfo.h"
#include "VectorDrawable.h"
+#include "thread/CommonPool.h"
#include "utils/TraceUtils.h"
#include <unistd.h>
@@ -49,10 +50,6 @@
unpinImages();
}
-TaskManager* SkiaPipeline::getTaskManager() {
- return mRenderThread.cacheManager().getTaskManager();
-}
-
void SkiaPipeline::onDestroyHardwareResources() {
unpinImages();
mRenderThread.cacheManager().trimStaleResources();
@@ -225,42 +222,21 @@
}
}
-class SkiaPipeline::SavePictureProcessor : public TaskProcessor<bool> {
-public:
- explicit SavePictureProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {}
-
- struct SavePictureTask : public Task<bool> {
- sk_sp<SkData> data;
- std::string filename;
- };
-
- void savePicture(const sk_sp<SkData>& data, const std::string& filename) {
- sp<SavePictureTask> task(new SavePictureTask());
- task->data = data;
- task->filename = filename;
- TaskProcessor<bool>::add(task);
- }
-
- virtual void onProcess(const sp<Task<bool>>& task) override {
- ATRACE_NAME("SavePictureTask");
- SavePictureTask* t = static_cast<SavePictureTask*>(task.get());
-
- if (0 == access(t->filename.c_str(), F_OK)) {
- task->setResult(false);
+static void savePictureAsync(const sk_sp<SkData>& data, const std::string& filename) {
+ CommonPool::post([data, filename] {
+ if (0 == access(filename.c_str(), F_OK)) {
return;
}
- SkFILEWStream stream(t->filename.c_str());
+ SkFILEWStream stream(filename.c_str());
if (stream.isValid()) {
- stream.write(t->data->data(), t->data->size());
+ stream.write(data->data(), data->size());
stream.flush();
SkDebugf("SKP Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(),
- t->filename.c_str());
+ filename.c_str());
}
-
- task->setResult(true);
- }
-};
+ });
+}
SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) {
if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
@@ -297,16 +273,10 @@
ATRACE_END();
// offload saving to file in a different thread
- if (!mSavePictureProcessor.get()) {
- TaskManager* taskManager = getTaskManager();
- mSavePictureProcessor = new SavePictureProcessor(
- taskManager->canRunTasks() ? taskManager : nullptr);
- }
if (1 == mCaptureSequence) {
- mSavePictureProcessor->savePicture(data, mCapturedFile);
+ savePictureAsync(data, mCapturedFile);
} else {
- mSavePictureProcessor->savePicture(
- data,
+ savePictureAsync(data,
mCapturedFile + "_" + std::to_string(mCaptureSequence));
}
mCaptureSequence--;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 7381e04..41d8646 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -33,8 +33,6 @@
explicit SkiaPipeline(renderthread::RenderThread& thread);
virtual ~SkiaPipeline();
- TaskManager* getTaskManager() override;
-
void onDestroyHardwareResources() override;
bool pinImages(std::vector<SkImage*>& mutableImages) override;
@@ -157,11 +155,7 @@
* mCaptureSequence counts how many frames are left to take in the sequence.
*/
int mCaptureSequence = 0;
- /**
- * mSavePictureProcessor is used to run the file saving code in a separate thread.
- */
- class SavePictureProcessor;
- sp<SavePictureProcessor> mSavePictureProcessor;
+
/**
* mRecorder holds the current picture recorder. We could store it on the stack to support
* parallel tryCapture calls (not really needed).
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 6c04232..8b02c11 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -23,6 +23,7 @@
#include "pipeline/skia/SkiaMemoryTracer.h"
#include "Properties.h"
#include "renderstate/RenderState.h"
+#include "thread/CommonPool.h"
#include <GrContextOptions.h>
#include <SkExecutor.h>
@@ -76,29 +77,15 @@
mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes);
}
-class CacheManager::SkiaTaskProcessor : public TaskProcessor<bool>, public SkExecutor {
+class CommonPoolExecutor : public SkExecutor {
public:
- explicit SkiaTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {}
-
- // This is really a Task<void> but that doesn't really work when Future<>
- // expects to be able to get/set a value
- struct SkiaTask : public Task<bool> {
- std::function<void()> func;
- };
-
virtual void add(std::function<void(void)> func) override {
- sp<SkiaTask> task(new SkiaTask());
- task->func = func;
- TaskProcessor<bool>::add(task);
- }
-
- virtual void onProcess(const sp<Task<bool> >& task) override {
- SkiaTask* t = static_cast<SkiaTask*>(task.get());
- t->func();
- task->setResult(true);
+ CommonPool::post(std::move(func));
}
};
+static CommonPoolExecutor sDefaultExecutor;
+
void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity, ssize_t size) {
contextOptions->fAllowPathMaskCaching = true;
@@ -107,12 +94,7 @@
// provided to Skia.
contextOptions->fGlyphCacheTextureMaximumBytes = GrNextSizePow2(mMaxSurfaceArea);
- if (mTaskManager.canRunTasks()) {
- if (!mTaskProcessor.get()) {
- mTaskProcessor = new SkiaTaskProcessor(&mTaskManager);
- }
- contextOptions->fExecutor = mTaskProcessor.get();
- }
+ contextOptions->fExecutor = &sDefaultExecutor;
auto& cache = skiapipeline::ShaderCache::get();
cache.initShaderDiskCache(identity, size);
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 66f04f1..b0286e8 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -24,8 +24,6 @@
#include <vector>
#include "pipeline/skia/VectorDrawableAtlas.h"
-#include "thread/TaskManager.h"
-#include "thread/TaskProcessor.h"
namespace android {
@@ -54,8 +52,6 @@
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
- TaskManager* getTaskManager() { return &mTaskManager; }
-
private:
friend class RenderThread;
@@ -78,10 +74,6 @@
};
sp<skiapipeline::VectorDrawableAtlas> mVectorDrawableAtlas;
-
- class SkiaTaskProcessor;
- sp<SkiaTaskProcessor> mTaskProcessor;
- TaskManager mTaskManager;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f5b4d93..baa41c1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -27,8 +27,10 @@
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaPipeline.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
+#include "thread/CommonPool.h"
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
+#include "utils/TraceUtils.h"
#include "../Properties.h"
#include <cutils/properties.h>
@@ -603,31 +605,14 @@
if (mFrameFences.size()) {
ATRACE_CALL();
for (auto& fence : mFrameFences) {
- fence->getResult();
+ fence.get();
}
mFrameFences.clear();
}
}
-class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> {
-public:
- explicit FuncTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {}
-
- virtual void onProcess(const sp<Task<bool> >& task) override {
- FuncTask* t = static_cast<FuncTask*>(task.get());
- t->func();
- task->setResult(true);
- }
-};
-
void CanvasContext::enqueueFrameWork(std::function<void()>&& func) {
- if (!mFrameWorkProcessor.get()) {
- mFrameWorkProcessor = new FuncTaskProcessor(mRenderPipeline->getTaskManager());
- }
- sp<FuncTask> task(new FuncTask());
- task->func = func;
- mFrameFences.push_back(task);
- mFrameWorkProcessor->add(task);
+ mFrameFences.push_back(CommonPool::async(std::move(func)));
}
int64_t CanvasContext::getFrameNumber() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 4da0eac..abca342 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -28,8 +28,6 @@
#include "ReliableSurface.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
-#include "thread/Task.h"
-#include "thread/TaskProcessor.h"
#include <EGL/egl.h>
#include <SkBitmap.h>
@@ -42,6 +40,7 @@
#include <set>
#include <string>
#include <vector>
+#include <future>
namespace android {
namespace uirenderer {
@@ -274,15 +273,7 @@
// Stores the bounds of the main content.
Rect mContentDrawBounds;
- // TODO: This is really a Task<void> but that doesn't really work
- // when Future<> expects to be able to get/set a value
- struct FuncTask : public Task<bool> {
- std::function<void()> func;
- };
- class FuncTaskProcessor;
-
- std::vector<sp<FuncTask>> mFrameFences;
- sp<TaskProcessor<bool>> mFrameWorkProcessor;
+ std::vector<std::future<void>> mFrameFences;
std::unique_ptr<IRenderPipeline> mRenderPipeline;
std::vector<std::function<void(int64_t)>> mFrameCompleteCallbacks;
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 2cfc8df3..0502eb8 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -75,7 +75,6 @@
virtual void renderLayers(const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, bool opaque,
const LightInfo& lightInfo) = 0;
- virtual TaskManager* getTaskManager() = 0;
virtual bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
ErrorHandler* errorHandler) = 0;
virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 34f76d9..1bcb819 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -31,6 +31,7 @@
#include "renderthread/RenderThread.h"
#include "utils/Macros.h"
#include "utils/TimeUtils.h"
+#include "utils/TraceUtils.h"
#include <ui/GraphicBuffer.h>
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index c784d64..def805a 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -28,6 +28,7 @@
#include "renderstate/RenderState.h"
#include "utils/FatVector.h"
#include "utils/TimeUtils.h"
+#include "utils/TraceUtils.h"
#ifdef HWUI_GLES_WRAP_ENABLED
#include "debug/GlesDriver.h"
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 6dacc7a..9916da5 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -23,6 +23,7 @@
#include "RenderThread.h"
#include "renderstate/RenderState.h"
#include "utils/FatVector.h"
+#include "utils/TraceUtils.h"
#include <GrBackendSemaphore.h>
#include <GrBackendSurface.h>
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 0e61899e..b45dbc8 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -21,6 +21,7 @@
#include "tests/common/TestContext.h"
#include "tests/common/TestScene.h"
#include "tests/common/scenes/TestSceneBase.h"
+#include "utils/TraceUtils.h"
#include <benchmark/benchmark.h>
#include <gui/Surface.h>
diff --git a/libs/hwui/tests/microbench/TaskManagerBench.cpp b/libs/hwui/tests/microbench/TaskManagerBench.cpp
deleted file mode 100644
index 4153bae..0000000
--- a/libs/hwui/tests/microbench/TaskManagerBench.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <benchmark/benchmark.h>
-
-#include "thread/Task.h"
-#include "thread/TaskManager.h"
-#include "thread/TaskProcessor.h"
-#include "thread/ThreadBase.h"
-
-#include <atomic>
-#include <vector>
-
-using namespace android;
-using namespace android::uirenderer;
-
-class TrivialTask : public Task<char> {};
-
-class TrivialProcessor : public TaskProcessor<char> {
-public:
- explicit TrivialProcessor(TaskManager* manager) : TaskProcessor(manager) {}
- virtual ~TrivialProcessor() {}
- virtual void onProcess(const sp<Task<char>>& task) override {
- TrivialTask* t = static_cast<TrivialTask*>(task.get());
- t->setResult(reinterpret_cast<intptr_t>(t) % 16 == 0 ? 'a' : 'b');
- }
-};
-
-class TestThread : public ThreadBase, public virtual RefBase {};
-
-void BM_TaskManager_allocateTask(benchmark::State& state) {
- std::vector<sp<TrivialTask>> tasks;
- tasks.reserve(state.max_iterations);
-
- while (state.KeepRunning()) {
- tasks.emplace_back(new TrivialTask);
- benchmark::DoNotOptimize(tasks.back());
- }
-}
-BENCHMARK(BM_TaskManager_allocateTask);
-
-void BM_TaskManager_enqueueTask(benchmark::State& state) {
- TaskManager taskManager;
- sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager));
- std::vector<sp<TrivialTask>> tasks;
- tasks.reserve(state.max_iterations);
-
- while (state.KeepRunning()) {
- tasks.emplace_back(new TrivialTask);
- benchmark::DoNotOptimize(tasks.back());
- processor->add(tasks.back());
- }
-
- for (sp<TrivialTask>& task : tasks) {
- task->getResult();
- }
-}
-BENCHMARK(BM_TaskManager_enqueueTask);
-
-void BM_TaskManager_enqueueRunDeleteTask(benchmark::State& state) {
- TaskManager taskManager;
- sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager));
- std::vector<sp<TrivialTask>> tasks;
- tasks.reserve(state.max_iterations);
-
- while (state.KeepRunning()) {
- tasks.emplace_back(new TrivialTask);
- benchmark::DoNotOptimize(tasks.back());
- processor->add(tasks.back());
- }
- state.ResumeTiming();
- for (sp<TrivialTask>& task : tasks) {
- benchmark::DoNotOptimize(task->getResult());
- }
- tasks.clear();
- state.PauseTiming();
-}
-BENCHMARK(BM_TaskManager_enqueueRunDeleteTask);
-
-void BM_Thread_enqueueTask(benchmark::State& state) {
- sp<TestThread> thread{new TestThread};
- thread->start();
-
- atomic_int counter(0);
- int expected = 0;
- while (state.KeepRunning()) {
- expected++;
- thread->queue().post([&counter]() { counter++; });
- }
- thread->queue().runSync([]() {});
-
- thread->requestExit();
- thread->join();
- if (counter != expected) {
- printf("Ran %d lambads, should have been %d\n", counter.load(), expected);
- }
-}
-BENCHMARK(BM_Thread_enqueueTask);
-
-void BM_Thread_enqueueRunDeleteTask(benchmark::State& state) {
- sp<TestThread> thread{new TestThread};
- thread->start();
- std::vector<std::future<int>> tasks;
- tasks.reserve(state.max_iterations);
-
- int expected = 0;
- while (state.KeepRunning()) {
- tasks.emplace_back(thread->queue().async([expected]() -> int { return expected + 1; }));
- expected++;
- }
- state.ResumeTiming();
- expected = 0;
- for (auto& future : tasks) {
- if (future.get() != ++expected) {
- printf("Mismatch expected %d vs. observed %d\n", expected, future.get());
- }
- }
- tasks.clear();
- state.PauseTiming();
-}
-BENCHMARK(BM_Thread_enqueueRunDeleteTask);
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/CommonPoolTests.cpp b/libs/hwui/tests/unit/CommonPoolTests.cpp
new file mode 100644
index 0000000..c564ed6
--- /dev/null
+++ b/libs/hwui/tests/unit/CommonPoolTests.cpp
@@ -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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "thread/CommonPool.h"
+
+#include <array>
+#include <condition_variable>
+#include <set>
+#include <thread>
+#include "unistd.h"
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(CommonPool, post) {
+ std::atomic_bool ran(false);
+ CommonPool::post([&ran] { ran = true; });
+ for (int i = 0; !ran && i < 1000; i++) {
+ usleep(1);
+ }
+ EXPECT_TRUE(ran) << "Failed to flip atomic after 1 second";
+}
+
+TEST(CommonPool, threadCount) {
+ std::set<pid_t> threads;
+ std::array<std::future<pid_t>, 64> futures;
+ for (int i = 0; i < futures.size(); i++) {
+ futures[i] = CommonPool::async([] {
+ usleep(10);
+ return gettid();
+ });
+ }
+ for (auto& f : futures) {
+ threads.insert(f.get());
+ }
+ EXPECT_EQ(threads.size(), CommonPool::THREAD_COUNT);
+ EXPECT_EQ(0, threads.count(gettid()));
+}
+
+TEST(CommonPool, singleThread) {
+ std::mutex mutex;
+ std::condition_variable fence;
+ bool isProcessing = false;
+ bool queuedSecond = false;
+
+ auto f1 = CommonPool::async([&] {
+ {
+ std::unique_lock lock{mutex};
+ isProcessing = true;
+ fence.notify_all();
+ while (!queuedSecond) {
+ fence.wait(lock);
+ }
+ }
+ return gettid();
+ });
+
+ {
+ std::unique_lock lock{mutex};
+ while (!isProcessing) {
+ fence.wait(lock);
+ }
+ }
+
+ auto f2 = CommonPool::async([] {
+ return gettid();
+ });
+
+ {
+ std::unique_lock lock{mutex};
+ queuedSecond = true;
+ fence.notify_all();
+ }
+
+ auto tid1 = f1.get();
+ auto tid2 = f2.get();
+ EXPECT_EQ(tid1, tid2);
+ EXPECT_NE(gettid(), tid1);
+}
+
+TEST(CommonPool, fullQueue) {
+ std::mutex lock;
+ std::condition_variable fence;
+ bool signaled = false;
+ static constexpr auto QUEUE_COUNT = CommonPool::THREAD_COUNT + CommonPool::QUEUE_SIZE + 10;
+ std::atomic_int queuedCount{0};
+ std::array<std::future<void>, QUEUE_COUNT> futures;
+
+ std::thread queueThread{[&] {
+ for (int i = 0; i < QUEUE_COUNT; i++) {
+ futures[i] = CommonPool::async([&] {
+ std::unique_lock _lock{lock};
+ while (!signaled) {
+ fence.wait(_lock);
+ }
+ });
+ queuedCount++;
+ }
+ }};
+
+ int previous;
+ do {
+ previous = queuedCount.load();
+ usleep(10000);
+ } while (previous != queuedCount.load());
+
+ EXPECT_GT(queuedCount.load(), CommonPool::QUEUE_SIZE);
+ EXPECT_LT(queuedCount.load(), QUEUE_COUNT);
+
+ {
+ std::unique_lock _lock{lock};
+ signaled = true;
+ fence.notify_all();
+ }
+
+ queueThread.join();
+ EXPECT_EQ(queuedCount.load(), QUEUE_COUNT);
+
+ // Ensure all our tasks are finished before return as they have references to the stack
+ for (auto& f : futures) {
+ f.get();
+ }
+}
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 63d1540..83d888c 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -22,9 +22,6 @@
#include "debug/NullGlesDriver.h"
#include "hwui/Typeface.h"
#include "tests/common/LeakChecker.h"
-#include "thread/Task.h"
-#include "thread/TaskManager.h"
-#include "thread/TaskProcessor.h"
#include <signal.h>
diff --git a/libs/hwui/thread/Barrier.h b/libs/hwui/thread/Barrier.h
deleted file mode 100644
index bb750ca..0000000
--- a/libs/hwui/thread/Barrier.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#ifndef ANDROID_HWUI_BARRIER_H
-#define ANDROID_HWUI_BARRIER_H
-
-#include <utils/Condition.h>
-
-namespace android {
-namespace uirenderer {
-
-class Barrier {
-public:
- explicit Barrier(Condition::WakeUpType type = Condition::WAKE_UP_ALL)
- : mType(type), mOpened(false) {}
- ~Barrier() {}
-
- void open() {
- Mutex::Autolock l(mLock);
- mOpened = true;
- mCondition.signal(mType);
- }
-
- void wait() const {
- Mutex::Autolock l(mLock);
- while (!mOpened) {
- mCondition.wait(mLock);
- }
- }
-
-private:
- Condition::WakeUpType mType;
- volatile bool mOpened;
- mutable Mutex mLock;
- mutable Condition mCondition;
-};
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // ANDROID_HWUI_BARRIER_H
diff --git a/libs/hwui/thread/CommonPool.cpp b/libs/hwui/thread/CommonPool.cpp
new file mode 100644
index 0000000..7f94a15
--- /dev/null
+++ b/libs/hwui/thread/CommonPool.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#include "CommonPool.h"
+
+#include <sys/resource.h>
+#include <utils/Trace.h>
+#include "renderthread/RenderThread.h"
+
+#include <array>
+
+namespace android {
+namespace uirenderer {
+
+CommonPool::CommonPool() {
+ ATRACE_CALL();
+
+ CommonPool* pool = this;
+ // Create 2 workers
+ for (int i = 0; i < THREAD_COUNT; i++) {
+ std::thread worker([pool, i] {
+ {
+ std::array<char, 20> name{"hwuiTask"};
+ snprintf(name.data(), name.size(), "hwuiTask%d", i);
+ auto self = pthread_self();
+ pthread_setname_np(self, name.data());
+ setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
+ auto startHook = renderthread::RenderThread::getOnStartHook();
+ if (startHook) {
+ startHook(name.data());
+ }
+ }
+ pool->workerLoop();
+ });
+ worker.detach();
+ }
+}
+
+void CommonPool::post(Task&& task) {
+ static CommonPool pool;
+ pool.enqueue(std::move(task));
+}
+
+void CommonPool::enqueue(Task&& task) {
+ std::unique_lock lock(mLock);
+ while (!mWorkQueue.hasSpace()) {
+ lock.unlock();
+ usleep(100);
+ lock.lock();
+ }
+ mWorkQueue.push(std::move(task));
+ if (mWaitingThreads == THREAD_COUNT || (mWaitingThreads > 0 && mWorkQueue.size() > 1)) {
+ mCondition.notify_one();
+ }
+}
+
+void CommonPool::workerLoop() {
+ std::unique_lock lock(mLock);
+ while (true) {
+ if (!mWorkQueue.hasWork()) {
+ mWaitingThreads++;
+ mCondition.wait(lock);
+ mWaitingThreads--;
+ }
+ // Need to double-check that work is still available now that we have the lock
+ // It may have already been grabbed by a different thread
+ while (mWorkQueue.hasWork()) {
+ auto work = mWorkQueue.pop();
+ lock.unlock();
+ work();
+ lock.lock();
+ }
+ }
+}
+
+} // namespace uirenderer
+} // namespace android
\ No newline at end of file
diff --git a/libs/hwui/thread/CommonPool.h b/libs/hwui/thread/CommonPool.h
new file mode 100644
index 0000000..aef2990
--- /dev/null
+++ b/libs/hwui/thread/CommonPool.h
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_COMMONPOOL_H
+#define FRAMEWORKS_BASE_COMMONPOOL_H
+
+#include "utils/Macros.h"
+
+#include <log/log.h>
+
+#include <condition_variable>
+#include <functional>
+#include <future>
+#include <mutex>
+
+namespace android {
+namespace uirenderer {
+
+template <class T, int SIZE>
+class ArrayQueue {
+ PREVENT_COPY_AND_ASSIGN(ArrayQueue);
+ static_assert(SIZE > 0, "Size must be positive");
+
+public:
+ ArrayQueue() = default;
+ ~ArrayQueue() = default;
+
+ constexpr size_t capacity() const { return SIZE; }
+ constexpr bool hasWork() const { return mHead != mTail; }
+ constexpr bool hasSpace() const { return ((mHead + 1) % SIZE) != mTail; }
+ constexpr int size() const {
+ if (mHead > mTail) {
+ return mHead - mTail;
+ } else {
+ return mTail - mHead + SIZE;
+ }
+ }
+
+ constexpr void push(T&& t) {
+ int newHead = (mHead + 1) % SIZE;
+ LOG_ALWAYS_FATAL_IF(newHead == mTail, "no space");
+
+ mBuffer[mHead] = std::move(t);
+ mHead = newHead;
+ }
+
+ constexpr T&& pop() {
+ LOG_ALWAYS_FATAL_IF(mTail == mHead, "empty");
+ int index = mTail;
+ mTail = (mTail + 1) % SIZE;
+ return std::move(mBuffer[index]);
+ }
+
+private:
+ T mBuffer[SIZE];
+ int mHead = 0;
+ int mTail = 0;
+};
+
+class CommonPool {
+ PREVENT_COPY_AND_ASSIGN(CommonPool);
+
+public:
+ using Task = std::function<void()>;
+ static constexpr auto THREAD_COUNT = 2;
+ static constexpr auto QUEUE_SIZE = 128;
+
+ static void post(Task&& func);
+
+ template <class F>
+ static auto async(F&& func) -> std::future<decltype(func())> {
+ typedef std::packaged_task<decltype(func())()> task_t;
+ auto task = std::make_shared<task_t>(std::forward<F>(func));
+ post([task]() { std::invoke(*task); });
+ return task->get_future();
+ }
+
+ template <class F>
+ static auto runSync(F&& func) -> decltype(func()) {
+ std::packaged_task<decltype(func())()> task{std::forward<F>(func)};
+ post([&task]() { std::invoke(task); });
+ return task.get_future().get();
+ };
+
+private:
+ CommonPool();
+ ~CommonPool() {}
+
+ void enqueue(Task&&);
+
+ void workerLoop();
+
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ int mWaitingThreads = 0;
+ ArrayQueue<Task, QUEUE_SIZE> mWorkQueue;
+};
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // FRAMEWORKS_BASE_COMMONPOOL_H
diff --git a/libs/hwui/thread/Future.h b/libs/hwui/thread/Future.h
deleted file mode 100644
index df53348e..0000000
--- a/libs/hwui/thread/Future.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#ifndef ANDROID_HWUI_FUTURE_H
-#define ANDROID_HWUI_FUTURE_H
-
-#include <utils/RefBase.h>
-
-#include "Barrier.h"
-
-namespace android {
-namespace uirenderer {
-
-template <typename T>
-class Future : public LightRefBase<Future<T> > {
-public:
- explicit Future(Condition::WakeUpType type = Condition::WAKE_UP_ONE)
- : mBarrier(type), mResult() {}
- ~Future() {}
-
- /**
- * Returns the result of this future, blocking if
- * the result is not available yet.
- */
- T get() const {
- mBarrier.wait();
- return mResult;
- }
-
- /**
- * This method must be called only once.
- */
- void produce(T result) {
- mResult = result;
- mBarrier.open();
- }
-
-private:
- Barrier mBarrier;
- T mResult;
-};
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // ANDROID_HWUI_FUTURE_H
diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h
deleted file mode 100644
index 6d33ac4..0000000
--- a/libs/hwui/thread/Signal.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#ifndef ANDROID_HWUI_SIGNAL_H
-#define ANDROID_HWUI_SIGNAL_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/threads.h>
-
-namespace android {
-namespace uirenderer {
-
-class Signal {
-public:
- explicit Signal(Condition::WakeUpType type = Condition::WAKE_UP_ALL)
- : mType(type), mSignaled(false) {}
- ~Signal() {}
-
- void signal() {
- {
- Mutex::Autolock l(mLock);
- mSignaled = true;
- }
- mCondition.signal(mType);
- }
-
- void wait() {
- Mutex::Autolock l(mLock);
- while (!mSignaled) {
- mCondition.wait(mLock);
- }
- mSignaled = false;
- }
-
-private:
- Condition::WakeUpType mType;
- volatile bool mSignaled;
- mutable Mutex mLock;
- mutable Condition mCondition;
-};
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // ANDROID_HWUI_SIGNAL_H
diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h
deleted file mode 100644
index 228ce19..0000000
--- a/libs/hwui/thread/Task.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#ifndef ANDROID_HWUI_TASK_H
-#define ANDROID_HWUI_TASK_H
-
-#include <utils/RefBase.h>
-#include <utils/Trace.h>
-
-#include "Future.h"
-
-namespace android {
-namespace uirenderer {
-
-class TaskBase : public RefBase {
-public:
- TaskBase() {}
- virtual ~TaskBase() {}
-};
-
-template <typename T>
-class Task : public TaskBase {
-public:
- Task() : mFuture(new Future<T>()) {}
- virtual ~Task() {}
-
- T getResult() const { return mFuture->get(); }
-
- void setResult(T result) { mFuture->produce(result); }
-
-protected:
- const sp<Future<T> >& future() const { return mFuture; }
-
-private:
- sp<Future<T> > mFuture;
-};
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // ANDROID_HWUI_TASK_H
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
deleted file mode 100644
index de10ff1..0000000
--- a/libs/hwui/thread/TaskManager.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#include <sys/resource.h>
-#include <sys/sysinfo.h>
-
-#include "Task.h"
-#include "TaskManager.h"
-#include "TaskProcessor.h"
-#include "utils/MathUtils.h"
-#include "renderthread/RenderThread.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Manager
-///////////////////////////////////////////////////////////////////////////////
-
-TaskManager::TaskManager() {
- // Get the number of available CPUs. This value does not change over time.
- int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
-
- // Really no point in making more than 2 of these worker threads, but
- // we do want to limit ourselves to 1 worker thread on dual-core devices.
- int workerCount = cpuCount > 2 ? 2 : 1;
- for (int i = 0; i < workerCount; i++) {
- String8 name;
- name.appendFormat("hwuiTask%d", i + 1);
- mThreads.push_back(new WorkerThread(name));
- }
-}
-
-TaskManager::~TaskManager() {
- for (size_t i = 0; i < mThreads.size(); i++) {
- mThreads[i]->exit();
- }
-}
-
-bool TaskManager::canRunTasks() const {
- return mThreads.size() > 0;
-}
-
-void TaskManager::stop() {
- for (size_t i = 0; i < mThreads.size(); i++) {
- mThreads[i]->exit();
- }
-}
-
-bool TaskManager::addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor) {
- if (mThreads.size() > 0) {
- TaskWrapper wrapper(task, processor);
-
- size_t minQueueSize = INT_MAX;
- sp<WorkerThread> thread;
-
- for (size_t i = 0; i < mThreads.size(); i++) {
- if (mThreads[i]->getTaskCount() < minQueueSize) {
- thread = mThreads[i];
- minQueueSize = mThreads[i]->getTaskCount();
- }
- }
-
- return thread->addTask(wrapper);
- }
- return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Thread
-///////////////////////////////////////////////////////////////////////////////
-
-status_t TaskManager::WorkerThread::readyToRun() {
- setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
- auto onStartHook = renderthread::RenderThread::getOnStartHook();
- if (onStartHook) {
- onStartHook(mName.c_str());
- }
-
- return NO_ERROR;
-}
-
-bool TaskManager::WorkerThread::threadLoop() {
- mSignal.wait();
- std::vector<TaskWrapper> tasks;
- {
- Mutex::Autolock l(mLock);
- tasks.swap(mTasks);
- }
-
- for (size_t i = 0; i < tasks.size(); i++) {
- const TaskWrapper& task = tasks[i];
- task.mProcessor->process(task.mTask);
- }
-
- return true;
-}
-
-bool TaskManager::WorkerThread::addTask(const TaskWrapper& task) {
- if (!isRunning()) {
- run(mName.string(), PRIORITY_DEFAULT);
- } else if (exitPending()) {
- return false;
- }
-
- {
- Mutex::Autolock l(mLock);
- mTasks.push_back(task);
- }
- mSignal.signal();
-
- return true;
-}
-
-size_t TaskManager::WorkerThread::getTaskCount() const {
- Mutex::Autolock l(mLock);
- return mTasks.size();
-}
-
-void TaskManager::WorkerThread::exit() {
- requestExit();
- mSignal.signal();
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
deleted file mode 100644
index 6c67222..0000000
--- a/libs/hwui/thread/TaskManager.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#ifndef ANDROID_HWUI_TASK_MANAGER_H
-#define ANDROID_HWUI_TASK_MANAGER_H
-
-#include <utils/Mutex.h>
-#include <utils/String8.h>
-#include <utils/Thread.h>
-
-#include "Signal.h"
-
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-template <typename T>
-class Task;
-class TaskBase;
-
-template <typename T>
-class TaskProcessor;
-class TaskProcessorBase;
-
-class TaskManager {
-public:
- TaskManager();
- ~TaskManager();
-
- /**
- * Returns true if this task manager can run tasks,
- * false otherwise. This method will typically return
- * false on a single CPU core device.
- */
- bool canRunTasks() const;
-
- /**
- * Stops all allocated threads. Adding tasks will start
- * the threads again as necessary.
- */
- void stop();
-
-private:
- template <typename T>
- friend class TaskProcessor;
-
- template <typename T>
- bool addTask(const sp<Task<T> >& task, const sp<TaskProcessor<T> >& processor) {
- return addTaskBase(sp<TaskBase>(task), sp<TaskProcessorBase>(processor));
- }
-
- bool addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor);
-
- struct TaskWrapper {
- TaskWrapper() : mTask(), mProcessor() {}
-
- TaskWrapper(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor)
- : mTask(task), mProcessor(processor) {}
-
- sp<TaskBase> mTask;
- sp<TaskProcessorBase> mProcessor;
- };
-
- class WorkerThread : public Thread {
- public:
- explicit WorkerThread(const String8& name)
- : Thread(false), mSignal(Condition::WAKE_UP_ONE), mName(name) {}
-
- bool addTask(const TaskWrapper& task);
- size_t getTaskCount() const;
- void exit();
-
- private:
- virtual status_t readyToRun() override;
- virtual bool threadLoop() override;
-
- // Lock for the list of tasks
- mutable Mutex mLock;
- std::vector<TaskWrapper> mTasks;
-
- // Signal used to wake up the thread when a new
- // task is available in the list
- mutable Signal mSignal;
-
- const String8 mName;
- };
-
- std::vector<sp<WorkerThread> > mThreads;
-};
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // ANDROID_HWUI_TASK_MANAGER_H
diff --git a/libs/hwui/thread/TaskProcessor.h b/libs/hwui/thread/TaskProcessor.h
deleted file mode 100644
index 8117ae6..0000000
--- a/libs/hwui/thread/TaskProcessor.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#ifndef ANDROID_HWUI_TASK_PROCESSOR_H
-#define ANDROID_HWUI_TASK_PROCESSOR_H
-
-#include <utils/RefBase.h>
-
-#include "Task.h"
-#include "TaskManager.h"
-
-namespace android {
-namespace uirenderer {
-
-class TaskProcessorBase : public RefBase {
-public:
- TaskProcessorBase() {}
- virtual ~TaskProcessorBase(){};
-
- virtual void process(const sp<TaskBase>& task) = 0;
-};
-
-template <typename T>
-class TaskProcessor : public TaskProcessorBase {
-public:
- explicit TaskProcessor(TaskManager* manager) : mManager(manager) {}
- virtual ~TaskProcessor() {}
-
- void add(const sp<Task<T> >& task) {
- if (!addImpl(task)) {
- // fall back to immediate execution
- process(task);
- }
- }
-
- virtual void onProcess(const sp<Task<T> >& task) = 0;
-
-private:
- bool addImpl(const sp<Task<T> >& task);
-
- virtual void process(const sp<TaskBase>& task) override {
- sp<Task<T> > realTask = static_cast<Task<T>*>(task.get());
- // This is the right way to do it but sp<> doesn't play nice
- // sp<Task<T> > realTask = static_cast<sp<Task<T> > >(task);
- onProcess(realTask);
- }
-
- TaskManager* mManager;
-};
-
-template <typename T>
-bool TaskProcessor<T>::addImpl(const sp<Task<T> >& task) {
- if (mManager) {
- sp<TaskProcessor<T> > self(this);
- return mManager->addTask(task, self);
- }
- return false;
-}
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_TASK_PROCESSOR_H
diff --git a/libs/hwui/thread/WorkQueue.h b/libs/hwui/thread/WorkQueue.h
index 7a6e638..42f8503 100644
--- a/libs/hwui/thread/WorkQueue.h
+++ b/libs/hwui/thread/WorkQueue.h
@@ -26,7 +26,6 @@
#include <functional>
#include <future>
#include <mutex>
-#include <variant>
#include <vector>
namespace android::uirenderer {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index dd179f3..050b2ec 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -167,11 +167,21 @@
/**
* Broadcast intent action when the set of enabled location providers changes. To check the
- * status of a provider, use {@link #isProviderEnabled(String)}.
+ * status of a provider, use {@link #isProviderEnabled(String)}. Depending on API level, may
+ * include a string intent extra, {@link #EXTRA_PROVIDER_NAME}, with the name of the provider
+ * whose state has changed. See {@link #EXTRA_PROVIDER_NAME} for the supported API level.
+ *
+ * @see #EXTRA_PROVIDER_NAME
*/
public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED";
/**
+ * Intent extra included with {@link #PROVIDERS_CHANGED_ACTION} broadcasts, containing the name
+ * of the location provider that has changed, to be used with location provider APIs.
+ */
+ public static final String EXTRA_PROVIDER_NAME = "android.location.extra.PROVIDER_NAME";
+
+ /**
* Broadcast intent action when the device location mode changes. To check the location mode,
* use {@link #isLocationEnabled()}.
*/
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 09cfa95..41e059b 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1014,9 +1014,9 @@
* frame indicates the number of samples per channel, e.g. 100 frames for a stereo compressed
* stream corresponds to 200 decoded interleaved PCM samples.
* @param delayInFrames number of frames to be ignored at the beginning of the stream. A value
- * of 0 indicates no padding is to be applied.
- * @param paddingInFrames number of frames to be ignored at the end of the stream. A value of 0
* of 0 indicates no delay is to be applied.
+ * @param paddingInFrames number of frames to be ignored at the end of the stream. A value of 0
+ * of 0 indicates no padding is to be applied.
*/
public void setOffloadDelayPadding(@IntRange(from = 0) int delayInFrames,
@IntRange(from = 0) int paddingInFrames) {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 4bc3897..2ec9355 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -3223,14 +3223,83 @@
// These constants were originally in-line with OMX values, but this
// correspondence is no longer maintained.
+ // Profiles and levels for AVC Codec, corresponding to the definitions in
+ // "SERIES H: AUDIOVISUAL AND MULTIMEDIA SYSTEMS,
+ // Infrastructure of audiovisual services – Coding of moving video
+ // Advanced video coding for generic audiovisual services"
+ // found at
+ // https://www.itu.int/rec/T-REC-H.264-201704-I
+
+ /**
+ * AVC Baseline profile.
+ * See definition in
+ * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>,
+ * Annex A.
+ */
public static final int AVCProfileBaseline = 0x01;
+
+ /**
+ * AVC Main profile.
+ * See definition in
+ * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>,
+ * Annex A.
+ */
public static final int AVCProfileMain = 0x02;
+
+ /**
+ * AVC Extended profile.
+ * See definition in
+ * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>,
+ * Annex A.
+ */
public static final int AVCProfileExtended = 0x04;
+
+ /**
+ * AVC High profile.
+ * See definition in
+ * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>,
+ * Annex A.
+ */
public static final int AVCProfileHigh = 0x08;
+
+ /**
+ * AVC High 10 profile.
+ * See definition in
+ * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>,
+ * Annex A.
+ */
public static final int AVCProfileHigh10 = 0x10;
+
+ /**
+ * AVC High 4:2:2 profile.
+ * See definition in
+ * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>,
+ * Annex A.
+ */
public static final int AVCProfileHigh422 = 0x20;
+
+ /**
+ * AVC High 4:4:4 profile.
+ * See definition in
+ * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>,
+ * Annex A.
+ */
public static final int AVCProfileHigh444 = 0x40;
+
+ /**
+ * AVC Constrained Baseline profile.
+ * See definition in
+ * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>,
+ * Annex A.
+ */
public static final int AVCProfileConstrainedBaseline = 0x10000;
+
+ /**
+ * AVC Constrained High profile.
+ * See definition in
+ * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>,
+ * Annex A.
+ */
public static final int AVCProfileConstrainedHigh = 0x80000;
public static final int AVCLevel1 = 0x01;
@@ -3420,8 +3489,34 @@
public static final int DolbyVisionLevelUhd48 = 0x80;
public static final int DolbyVisionLevelUhd60 = 0x100;
+ // Profiles and levels for AV1 Codec, corresponding to the
+ // definitions in
+ // "AV1 Bitstream & Decoding Process Specification", Annex A
+ // found at
+ // https://aomedia.org/av1-bitstream-and-decoding-process-specification/
+
+ /**
+ * AV1 Main profile.
+ * See definition in
+ * <a href="https://aomedia.org/av1-bitstream-and-decoding-process-specification/"> AV1 Bitstream and Decoding Process Specification</a>
+ * Annex A.
+ */
public static final int AV1Profile0 = 0x1;
+
+ /**
+ * AV1 High profile.
+ * See definition in
+ * <a href="https://aomedia.org/av1-bitstream-and-decoding-process-specification/"> AV1 Bitstream and Decoding Process Specification</a>
+ * Annex A.
+ */
public static final int AV1Profile1 = 0x2;
+
+ /**
+ * AV1 Professional profile.
+ * See definition in
+ * <a href="https://aomedia.org/av1-bitstream-and-decoding-process-specification/"> AV1 Bitstream and Decoding Process Specification</a>
+ * Annex A.
+ */
public static final int AV1Profile2 = 0x4;
public static final int AV1Level2 = 0x1;
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index d724762..3838a999 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -37,6 +37,7 @@
import java.net.UnknownServiceException;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
/** @hide */
public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
@@ -46,27 +47,23 @@
// connection timeout - 30 sec
private static final int CONNECT_TIMEOUT_MS = 30 * 1000;
- @UnsupportedAppUsage
- private long mCurrentOffset = -1;
- @UnsupportedAppUsage
- private URL mURL = null;
- @UnsupportedAppUsage
- private Map<String, String> mHeaders = null;
- @UnsupportedAppUsage
- private HttpURLConnection mConnection = null;
- @UnsupportedAppUsage
- private long mTotalSize = -1;
- private InputStream mInputStream = null;
-
- @UnsupportedAppUsage
- private boolean mAllowCrossDomainRedirect = true;
- @UnsupportedAppUsage
- private boolean mAllowCrossProtocolRedirect = true;
-
// from com.squareup.okhttp.internal.http
private final static int HTTP_TEMP_REDIRECT = 307;
private final static int MAX_REDIRECTS = 20;
+ class ConnectionState {
+ public HttpURLConnection mConnection = null;
+ public InputStream mInputStream = null;
+ public long mCurrentOffset = -1;
+ public Map<String, String> mHeaders = null;
+ public URL mURL = null;
+ public long mTotalSize = -1;
+ public boolean mAllowCrossDomainRedirect = true;
+ public boolean mAllowCrossProtocolRedirect = true;
+ }
+ private final AtomicReference<ConnectionState> mConnectionStateHolder =
+ new AtomicReference<ConnectionState>();
+
@UnsupportedAppUsage
public MediaHTTPConnection() {
CookieHandler cookieHandler = CookieHandler.getDefault();
@@ -84,13 +81,23 @@
Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers);
}
+ ConnectionState connectionState = mConnectionStateHolder.get();
+ synchronized (this) {
+ if (connectionState == null) {
+ connectionState = new ConnectionState();
+ mConnectionStateHolder.set(connectionState);
+ }
+ }
+
try {
disconnect();
- mAllowCrossDomainRedirect = true;
- mURL = new URL(uri);
- mHeaders = convertHeaderStringToMap(headers);
+ connectionState.mAllowCrossDomainRedirect = true;
+ connectionState.mURL = new URL(uri);
+ connectionState.mHeaders = convertHeaderStringToMap(headers, connectionState);
} catch (MalformedURLException e) {
return null;
+ } finally {
+ mConnectionStateHolder.set(connectionState);
}
return native_getIMemory();
@@ -106,18 +113,21 @@
}
/* returns true iff header is internal */
- private boolean filterOutInternalHeaders(String key, String val) {
+ private boolean filterOutInternalHeaders(
+ String key, String val, ConnectionState connectionState) {
if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) {
- mAllowCrossDomainRedirect = parseBoolean(val);
+ connectionState.mAllowCrossDomainRedirect = parseBoolean(val);
// cross-protocol redirects are also controlled by this flag
- mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect;
+ connectionState.mAllowCrossProtocolRedirect =
+ connectionState.mAllowCrossDomainRedirect;
} else {
return false;
}
return true;
}
- private Map<String, String> convertHeaderStringToMap(String headers) {
+ private Map<String, String> convertHeaderStringToMap(String headers,
+ ConnectionState connectionState) {
HashMap<String, String> map = new HashMap<String, String>();
String[] pairs = headers.split("\r\n");
@@ -127,7 +137,7 @@
String key = pair.substring(0, colonPos);
String val = pair.substring(colonPos + 1);
- if (!filterOutInternalHeaders(key, val)) {
+ if (!filterOutInternalHeaders(key, val, connectionState)) {
map.put(key, val);
}
}
@@ -139,25 +149,28 @@
@Override
@UnsupportedAppUsage
public void disconnect() {
- teardownConnection();
- mHeaders = null;
- mURL = null;
+ ConnectionState connectionState = mConnectionStateHolder.getAndSet(null);
+ if (connectionState != null) {
+ teardownConnection(connectionState);
+ connectionState.mHeaders = null;
+ connectionState.mURL = null;
+ }
}
- private void teardownConnection() {
- if (mConnection != null) {
- if (mInputStream != null) {
+ private void teardownConnection(ConnectionState connectionState) {
+ if (connectionState.mConnection != null) {
+ if (connectionState.mInputStream != null) {
try {
- mInputStream.close();
+ connectionState.mInputStream.close();
} catch (IOException e) {
}
- mInputStream = null;
+ connectionState.mInputStream = null;
}
- mConnection.disconnect();
- mConnection = null;
+ connectionState.mConnection.disconnect();
+ connectionState.mConnection = null;
- mCurrentOffset = -1;
+ connectionState.mCurrentOffset = -1;
}
}
@@ -184,42 +197,44 @@
return false;
}
- private void seekTo(long offset) throws IOException {
- teardownConnection();
+ private void seekTo(long offset, ConnectionState connectionState) throws IOException {
+ teardownConnection(connectionState);
try {
int response;
int redirectCount = 0;
- URL url = mURL;
+ URL url = connectionState.mURL;
// do not use any proxy for localhost (127.0.0.1)
boolean noProxy = isLocalHost(url);
while (true) {
if (noProxy) {
- mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
+ connectionState.mConnection =
+ (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
} else {
- mConnection = (HttpURLConnection)url.openConnection();
+ connectionState.mConnection = (HttpURLConnection) url.openConnection();
}
- mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS);
+ connectionState.mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS);
// handle redirects ourselves if we do not allow cross-domain redirect
- mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect);
+ connectionState.mConnection.setInstanceFollowRedirects(
+ connectionState.mAllowCrossDomainRedirect);
- if (mHeaders != null) {
- for (Map.Entry<String, String> entry : mHeaders.entrySet()) {
- mConnection.setRequestProperty(
+ if (connectionState.mHeaders != null) {
+ for (Map.Entry<String, String> entry : connectionState.mHeaders.entrySet()) {
+ connectionState.mConnection.setRequestProperty(
entry.getKey(), entry.getValue());
}
}
if (offset > 0) {
- mConnection.setRequestProperty(
+ connectionState.mConnection.setRequestProperty(
"Range", "bytes=" + offset + "-");
}
- response = mConnection.getResponseCode();
+ response = connectionState.mConnection.getResponseCode();
if (response != HttpURLConnection.HTTP_MULT_CHOICE &&
response != HttpURLConnection.HTTP_MOVED_PERM &&
response != HttpURLConnection.HTTP_MOVED_TEMP &&
@@ -233,7 +248,7 @@
throw new NoRouteToHostException("Too many redirects: " + redirectCount);
}
- String method = mConnection.getRequestMethod();
+ String method = connectionState.mConnection.getRequestMethod();
if (response == HTTP_TEMP_REDIRECT &&
!method.equals("GET") && !method.equals("HEAD")) {
// "If the 307 status code is received in response to a
@@ -241,34 +256,35 @@
// automatically redirect the request"
throw new NoRouteToHostException("Invalid redirect");
}
- String location = mConnection.getHeaderField("Location");
+ String location = connectionState.mConnection.getHeaderField("Location");
if (location == null) {
throw new NoRouteToHostException("Invalid redirect");
}
- url = new URL(mURL /* TRICKY: don't use url! */, location);
+ url = new URL(connectionState.mURL /* TRICKY: don't use url! */, location);
if (!url.getProtocol().equals("https") &&
!url.getProtocol().equals("http")) {
throw new NoRouteToHostException("Unsupported protocol redirect");
}
- boolean sameProtocol = mURL.getProtocol().equals(url.getProtocol());
- if (!mAllowCrossProtocolRedirect && !sameProtocol) {
+ boolean sameProtocol =
+ connectionState.mURL.getProtocol().equals(url.getProtocol());
+ if (!connectionState.mAllowCrossProtocolRedirect && !sameProtocol) {
throw new NoRouteToHostException("Cross-protocol redirects are disallowed");
}
- boolean sameHost = mURL.getHost().equals(url.getHost());
- if (!mAllowCrossDomainRedirect && !sameHost) {
+ boolean sameHost = connectionState.mURL.getHost().equals(url.getHost());
+ if (!connectionState.mAllowCrossDomainRedirect && !sameHost) {
throw new NoRouteToHostException("Cross-domain redirects are disallowed");
}
if (response != HTTP_TEMP_REDIRECT) {
// update effective URL, unless it is a Temporary Redirect
- mURL = url;
+ connectionState.mURL = url;
}
}
- if (mAllowCrossDomainRedirect) {
+ if (connectionState.mAllowCrossDomainRedirect) {
// remember the current, potentially redirected URL if redirects
// were handled by HttpURLConnection
- mURL = mConnection.getURL();
+ connectionState.mURL = connectionState.mConnection.getURL();
}
if (response == HttpURLConnection.HTTP_PARTIAL) {
@@ -276,10 +292,9 @@
// because what we want is not just the length of the range
// returned but the size of the full content if available.
- String contentRange =
- mConnection.getHeaderField("Content-Range");
+ String contentRange = connectionState.mConnection.getHeaderField("Content-Range");
- mTotalSize = -1;
+ connectionState.mTotalSize = -1;
if (contentRange != null) {
// format is "bytes xxx-yyy/zzz
// where "zzz" is the total number of bytes of the
@@ -291,7 +306,7 @@
contentRange.substring(lastSlashPos + 1);
try {
- mTotalSize = Long.parseLong(total);
+ connectionState.mTotalSize = Long.parseLong(total);
} catch (NumberFormatException e) {
}
}
@@ -299,7 +314,7 @@
} else if (response != HttpURLConnection.HTTP_OK) {
throw new IOException();
} else {
- mTotalSize = mConnection.getContentLength();
+ connectionState.mTotalSize = connectionState.mConnection.getContentLength();
}
if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) {
@@ -308,14 +323,14 @@
throw new ProtocolException();
}
- mInputStream =
- new BufferedInputStream(mConnection.getInputStream());
+ connectionState.mInputStream =
+ new BufferedInputStream(connectionState.mConnection.getInputStream());
- mCurrentOffset = offset;
+ connectionState.mCurrentOffset = offset;
} catch (IOException e) {
- mTotalSize = -1;
- teardownConnection();
- mCurrentOffset = -1;
+ connectionState.mTotalSize = -1;
+ teardownConnection(connectionState);
+ connectionState.mCurrentOffset = -1;
throw e;
}
@@ -324,10 +339,14 @@
@Override
@UnsupportedAppUsage
public int readAt(long offset, int size) {
- return native_readAt(offset, size);
+ ConnectionState connectionState = mConnectionStateHolder.get();
+ if (connectionState != null) {
+ return native_readAt(offset, size, connectionState);
+ }
+ return -1;
}
- private int readAt(long offset, byte[] data, int size) {
+ private int readAt(long offset, byte[] data, int size, ConnectionState connectionState) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
@@ -335,12 +354,12 @@
try {
synchronized(this) {
- if (offset != mCurrentOffset) {
- seekTo(offset);
+ if (offset != connectionState.mCurrentOffset) {
+ seekTo(offset, connectionState);
}
}
- int n = mInputStream.read(data, 0, size);
+ int n = connectionState.mInputStream.read(data, 0, size);
if (n == -1) {
// InputStream signals EOS using a -1 result, our semantics
@@ -348,7 +367,7 @@
n = 0;
}
- mCurrentOffset += n;
+ connectionState.mCurrentOffset += n;
if (VERBOSE) {
Log.d(TAG, "readAt " + offset + " / " + size + " => " + n);
@@ -380,35 +399,47 @@
@Override
public synchronized long getSize() {
- if (mConnection == null) {
- try {
- seekTo(0);
- } catch (IOException e) {
- return -1;
+ ConnectionState connectionState = mConnectionStateHolder.get();
+ if (connectionState != null) {
+ if (connectionState.mConnection == null) {
+ try {
+ seekTo(0, connectionState);
+ } catch (IOException e) {
+ return -1;
+ }
}
+ return connectionState.mTotalSize;
}
- return mTotalSize;
+ return -1;
}
@Override
@UnsupportedAppUsage
public synchronized String getMIMEType() {
- if (mConnection == null) {
- try {
- seekTo(0);
- } catch (IOException e) {
- return "application/octet-stream";
+ ConnectionState connectionState = mConnectionStateHolder.get();
+ if (connectionState != null) {
+ if (connectionState.mConnection == null) {
+ try {
+ seekTo(0, connectionState);
+ } catch (IOException e) {
+ return "application/octet-stream";
+ }
}
+ return connectionState.mConnection.getContentType();
}
- return mConnection.getContentType();
+ return null;
}
@Override
@UnsupportedAppUsage
public String getUri() {
- return mURL.toString();
+ ConnectionState connectionState = mConnectionStateHolder.get();
+ if (connectionState != null) {
+ return connectionState.mURL.toString();
+ }
+ return null;
}
@Override
@@ -421,7 +452,7 @@
private native final void native_finalize();
private native final IBinder native_getIMemory();
- private native final int native_readAt(long offset, int size);
+ private native int native_readAt(long offset, int size, ConnectionState connectionState);
static {
System.loadLibrary("media_jni");
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 474b671..dc2d177 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -54,6 +54,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.IntStream;
@@ -248,7 +249,7 @@
public MtpDatabase(Context context, String volumeName,
String[] subDirectories) {
native_setup();
- mContext = context;
+ mContext = Objects.requireNonNull(context);
mMediaProvider = context.getContentResolver()
.acquireContentProviderClient(MediaStore.AUTHORITY);
mVolumeName = volumeName;
@@ -294,6 +295,10 @@
}
}
+ public Context getContext() {
+ return mContext;
+ }
+
@Override
public void close() {
mManager.close();
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index a555d37..f0ab9a3 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -18,7 +18,12 @@
import com.android.internal.util.Preconditions;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.ByteStringUtils;
+
import java.io.FileDescriptor;
+import java.util.Random;
/**
* Java wrapper for MTP/PTP support as USB responder.
@@ -29,6 +34,12 @@
private long mNativeContext; // accessed by native methods
private final MtpDatabase mDatabase;
private final Runnable mOnTerminate;
+ private final Context mContext;
+
+// It requires "exactly 32 characters, including any leading 0s" in MTP spec
+// (5.1.1.14 Serial Number)
+ private static final int sID_LEN_BYTES = 16;
+ private static final int sID_LEN_STR = (sID_LEN_BYTES * 2);
static {
System.loadLibrary("media_jni");
@@ -41,10 +52,41 @@
Runnable onTerminate,
String deviceInfoManufacturer,
String deviceInfoModel,
- String deviceInfoDeviceVersion,
- String deviceInfoSerialNumber) {
+ String deviceInfoDeviceVersion) {
mDatabase = Preconditions.checkNotNull(database);
mOnTerminate = Preconditions.checkNotNull(onTerminate);
+ mContext = mDatabase.getContext();
+
+ final String strID_PREFS_NAME = "mtp-cfg";
+ final String strID_PREFS_KEY = "mtp-id";
+ String strRandomId = null;
+ String deviceInfoSerialNumber;
+
+ SharedPreferences sharedPref =
+ mContext.getSharedPreferences(strID_PREFS_NAME, Context.MODE_PRIVATE);
+ if (sharedPref.contains(strID_PREFS_KEY)) {
+ strRandomId = sharedPref.getString(strID_PREFS_KEY, null);
+
+ // Check for format consistence (regenerate upon corruption)
+ if (strRandomId.length() != sID_LEN_STR) {
+ strRandomId = null;
+ } else {
+ // Only accept hex digit
+ for (int ii = 0; ii < strRandomId.length(); ii++)
+ if (Character.digit(strRandomId.charAt(ii), 16) == -1) {
+ strRandomId = null;
+ break;
+ }
+ }
+ }
+
+ if (strRandomId == null) {
+ strRandomId = getRandId();
+ sharedPref.edit().putString(strID_PREFS_KEY, strRandomId).apply();
+ }
+
+ deviceInfoSerialNumber = strRandomId;
+
native_setup(
database,
controlFd,
@@ -56,6 +98,14 @@
database.setServer(this);
}
+ private String getRandId() {
+ Random randomVal = new Random();
+ byte[] randomBytes = new byte[sID_LEN_BYTES];
+
+ randomVal.nextBytes(randomBytes);
+ return ByteStringUtils.toHexString(randomBytes);
+ }
+
public void start() {
Thread thread = new Thread(this, "MtpServer");
thread.start();
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index 365e045..d28c15c 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -109,7 +109,8 @@
gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
CHECK(gFields.context != NULL);
- gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I");
+ gFields.readAtMethodID = env->GetMethodID(
+ clazz.get(), "readAt", "(J[BILandroid/media/MediaHTTPConnection$ConnectionState;)I");
}
static void android_media_MediaHTTPConnection_native_setup(
@@ -132,7 +133,7 @@
}
static jint android_media_MediaHTTPConnection_native_readAt(
- JNIEnv *env, jobject thiz, jlong offset, jint size) {
+ JNIEnv *env, jobject thiz, jlong offset, jint size, jobject connectionState) {
sp<JMediaHTTPConnection> conn = getObject(env, thiz);
if (size > JMediaHTTPConnection::kBufferSize) {
size = JMediaHTTPConnection::kBufferSize;
@@ -141,7 +142,7 @@
jbyteArray byteArrayObj = conn->getByteArrayObj();
jint n = env->CallIntMethod(
- thiz, gFields.readAtMethodID, offset, byteArrayObj, size);
+ thiz, gFields.readAtMethodID, offset, byteArrayObj, size, connectionState);
if (n > 0) {
env->GetByteArrayRegion(
@@ -158,7 +159,7 @@
{ "native_getIMemory", "()Landroid/os/IBinder;",
(void *)android_media_MediaHTTPConnection_native_getIMemory },
- { "native_readAt", "(JI)I",
+ { "native_readAt", "(JILandroid/media/MediaHTTPConnection$ConnectionState;)I",
(void *)android_media_MediaHTTPConnection_native_readAt },
{ "native_init", "()V",
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index b238ae4..9d91487 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -1615,12 +1615,12 @@
return;
}
// See if the data sub is registered for PS services on cell.
- final NetworkRegistrationInfo nrs = dataSs.getNetworkRegistrationInfo(
+ final NetworkRegistrationInfo nri = dataSs.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
latencyBroadcast.putExtra(
NetworkMonitorUtils.EXTRA_CELL_ID,
- nrs == null ? null : nrs.getCellIdentity());
+ nri == null ? null : nri.getCellIdentity());
latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE);
} else {
return;
diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
index 1e8cd5a..83557f2 100644
--- a/packages/SystemUI/res/layout/biometric_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -39,152 +39,158 @@
android:layout_height="0dp"
android:layout_weight="1" />
- <LinearLayout
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <!-- This is not a Space since Spaces cannot be clicked. The width of this changes depending
- on horizontal/portrait orientation -->
- <View
- android:id="@+id/left_space"
- android:layout_weight="1"
- android:layout_width="0dp"
- android:layout_height="match_parent"/>
+ android:layout_height="wrap_content">
<LinearLayout
- android:id="@+id/dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/biometric_dialog_bg"
- android:layout_marginBottom="@dimen/biometric_dialog_border_padding"
- android:layout_marginLeft="@dimen/biometric_dialog_border_padding"
- android:layout_marginRight="@dimen/biometric_dialog_border_padding">
+ android:orientation="horizontal">
- <TextView
- android:id="@+id/title"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="24dp"
- android:layout_marginStart="24dp"
- android:layout_marginTop="24dp"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:textSize="20sp"
- android:maxLines="1"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:textColor="?android:attr/textColorPrimary"/>
+ <!-- This is not a Space since Spaces cannot be clicked. The width of this changes
+ depending on horizontal/portrait orientation -->
+ <View
+ android:id="@+id/left_space"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginStart="24dp"
- android:layout_marginEnd="24dp"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:textSize="16sp"
- android:maxLines="1"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:textColor="?android:attr/textColorPrimary"/>
+ <LinearLayout
+ android:id="@+id/dialog"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="@drawable/biometric_dialog_bg"
+ android:layout_marginBottom="@dimen/biometric_dialog_border_padding"
+ android:layout_marginLeft="@dimen/biometric_dialog_border_padding"
+ android:layout_marginRight="@dimen/biometric_dialog_border_padding">
- <TextView
- android:id="@+id/description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="24dp"
- android:layout_marginStart="24dp"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:paddingTop="8dp"
- android:textSize="16sp"
- android:maxLines="4"
- android:textColor="?android:attr/textColorPrimary"/>
+ <TextView
+ android:id="@+id/title"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="24dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginTop="24dp"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:textSize="20sp"
+ android:maxLines="1"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:textColor="?android:attr/textColorPrimary"/>
- <ImageView
- android:id="@+id/biometric_icon"
- android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
- android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="48dp"
- android:scaleType="fitXY" />
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="24dp"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:textSize="16sp"
+ android:maxLines="1"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:textColor="?android:attr/textColorPrimary"/>
- <TextView
- android:id="@+id/error"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="24dp"
- android:layout_marginStart="24dp"
- android:paddingTop="16dp"
- android:paddingBottom="24dp"
- android:textSize="12sp"
- android:gravity="center_horizontal"
- android:accessibilityLiveRegion="polite"
- android:contentDescription="@string/accessibility_biometric_dialog_help_area"
- android:textColor="?android:attr/textColorSecondary"/>
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="24dp"
+ android:layout_marginStart="24dp"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:paddingTop="8dp"
+ android:textSize="16sp"
+ android:maxLines="4"
+ android:textColor="?android:attr/textColorPrimary"/>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="72dip"
- android:paddingTop="24dp"
- android:layout_gravity="center_vertical"
- style="?android:attr/buttonBarStyle"
- android:orientation="horizontal"
- android:measureWithLargestChild="true">
- <Space android:id="@+id/leftSpacer"
- android:layout_width="12dp"
- android:layout_height="match_parent"
- android:visibility="visible" />
- <!-- Negative Button -->
- <Button android:id="@+id/button2"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:gravity="center"
- android:maxLines="2" />
- <Space android:id="@+id/middleSpacer"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:visibility="visible" />
- <!-- Positive Button -->
- <Button android:id="@+id/button1"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:gravity="center"
- android:maxLines="2"
- android:text="@string/biometric_dialog_confirm"
- android:visibility="gone"/>
- <!-- Try Again Button -->
- <Button android:id="@+id/button_try_again"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:gravity="center"
- android:maxLines="2"
- android:text="@string/biometric_dialog_try_again"
- android:visibility="gone"/>
- <Space android:id="@+id/rightSpacer"
- android:layout_width="12dip"
- android:layout_height="match_parent"
- android:visibility="visible" />
- </LinearLayout>
+ <ImageView
+ android:id="@+id/biometric_icon"
+ android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
+ android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="48dp"
+ android:scaleType="fitXY" />
+
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="24dp"
+ android:layout_marginStart="24dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="24dp"
+ android:textSize="12sp"
+ android:gravity="center_horizontal"
+ android:accessibilityLiveRegion="polite"
+ android:contentDescription="@string/accessibility_biometric_dialog_help_area"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="72dip"
+ android:paddingTop="24dp"
+ android:layout_gravity="center_vertical"
+ style="?android:attr/buttonBarStyle"
+ android:orientation="horizontal"
+ android:measureWithLargestChild="true">
+ <Space android:id="@+id/leftSpacer"
+ android:layout_width="12dp"
+ android:layout_height="match_parent"
+ android:visibility="visible" />
+ <!-- Negative Button -->
+ <Button android:id="@+id/button2"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:gravity="center"
+ android:maxLines="2" />
+ <Space android:id="@+id/middleSpacer"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:visibility="visible" />
+ <!-- Positive Button -->
+ <Button android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:gravity="center"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_confirm"
+ android:visibility="gone"/>
+ <!-- Try Again Button -->
+ <Button android:id="@+id/button_try_again"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:gravity="center"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_try_again"
+ android:visibility="gone"/>
+ <Space android:id="@+id/rightSpacer"
+ android:layout_width="12dip"
+ android:layout_height="match_parent"
+ android:visibility="visible" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <!-- This is not a Space since Spaces cannot be clicked. The width of this changes
+ depending on horizontal/portrait orientation -->
+ <View
+ android:id="@+id/right_space"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" />
+
</LinearLayout>
- <!-- This is not a Space since Spaces cannot be clicked. The width of this changes depending
- on horizontal/portrait orientation -->
- <View
- android:id="@+id/right_space"
- android:layout_weight="1"
- android:layout_width="0dp"
- android:layout_height="match_parent" />
-
- </LinearLayout>
+ </ScrollView>
</LinearLayout>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index d40fa66..ce65b5a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -60,8 +60,8 @@
*/
private ViewGroup mBigClockContainer;
/**
- * Status area (date and other stuff) shown below the clock. Plugin can decide whether
- * or not to show it below the alternate clock.
+ * Status area (date and other stuff) shown below the clock. Plugin can decide whether or not to
+ * show it below the alternate clock.
*/
private View mKeyguardStatusArea;
/**
@@ -75,24 +75,19 @@
private boolean mSupportsDarkText;
private int[] mColorPalette;
+ /**
+ * Track the state of the status bar to know when to hide the big_clock_container.
+ */
+ private int mStatusBarState;
+
private final StatusBarStateController.StateListener mStateListener =
new StatusBarStateController.StateListener() {
@Override
public void onStateChanged(int newState) {
- if (mBigClockContainer == null) {
- return;
- }
- if (newState == StatusBarState.SHADE) {
- if (mBigClockContainer.getVisibility() == View.VISIBLE) {
- mBigClockContainer.setVisibility(View.INVISIBLE);
- }
- } else {
- if (mBigClockContainer.getVisibility() == View.INVISIBLE) {
- mBigClockContainer.setVisibility(View.VISIBLE);
- }
- }
+ mStatusBarState = newState;
+ updateBigClockVisibility();
}
- };
+ };
private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
@@ -139,7 +134,9 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Dependency.get(ClockManager.class).addOnClockChangedListener(mClockChangedListener);
- Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
+ StatusBarStateController stateController = Dependency.get(StatusBarStateController.class);
+ stateController.addCallback(mStateListener);
+ mStateListener.onStateChanged(stateController.getState());
SysuiColorExtractor colorExtractor = Dependency.get(SysuiColorExtractor.class);
colorExtractor.addOnColorsChangedListener(mColorsListener);
updateColors(colorExtractor);
@@ -151,7 +148,7 @@
Dependency.get(ClockManager.class).removeOnClockChangedListener(mClockChangedListener);
Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
Dependency.get(SysuiColorExtractor.class)
- .removeOnColorsChangedListener(mColorsListener);
+ .removeOnColorsChangedListener(mColorsListener);
setClockPlugin(null);
}
@@ -164,7 +161,7 @@
}
if (mBigClockContainer != null) {
mBigClockContainer.removeAllViews();
- mBigClockContainer.setVisibility(View.GONE);
+ updateBigClockVisibility();
}
mClockPlugin = null;
}
@@ -184,7 +181,7 @@
View bigClockView = plugin.getBigClockView();
if (bigClockView != null && mBigClockContainer != null) {
mBigClockContainer.addView(bigClockView);
- mBigClockContainer.setVisibility(View.VISIBLE);
+ updateBigClockVisibility();
}
// Hide default clock.
if (!plugin.shouldShowStatusArea()) {
@@ -208,12 +205,10 @@
View bigClockView = mClockPlugin.getBigClockView();
if (bigClockView != null) {
container.addView(bigClockView);
- if (container.getVisibility() == View.GONE) {
- container.setVisibility(View.VISIBLE);
- }
}
}
mBigClockContainer = container;
+ updateBigClockVisibility();
}
/**
@@ -254,6 +249,7 @@
/**
* Set the amount (ratio) that the device has transitioned to doze.
+ *
* @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
*/
public void setDarkAmount(float darkAmount) {
@@ -307,9 +303,24 @@
}
}
+ private void updateBigClockVisibility() {
+ if (mBigClockContainer == null) {
+ return;
+ }
+ final boolean inDisplayState = mStatusBarState == StatusBarState.KEYGUARD
+ || mStatusBarState == StatusBarState.SHADE_LOCKED;
+ final int visibility =
+ inDisplayState && mBigClockContainer.getChildCount() != 0 ? View.VISIBLE
+ : View.GONE;
+ if (mBigClockContainer.getVisibility() != visibility) {
+ mBigClockContainer.setVisibility(visibility);
+ }
+ }
+
/**
- * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock
- * in these cases.
+ * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock in
+ * these
+ * cases.
*/
public void setKeyguardShowingHeader(boolean hasHeader) {
if (mShowingHeader == hasHeader || hasCustomClock()) {
@@ -327,12 +338,12 @@
mClockView.getPaddingRight(), paddingBottom);
}
- @VisibleForTesting (otherwise = VisibleForTesting.NONE)
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
ClockManager.ClockChangedListener getClockChangedListener() {
return mClockChangedListener;
}
- @VisibleForTesting (otherwise = VisibleForTesting.NONE)
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
StatusBarStateController.StateListener getStateListener() {
return mStateListener;
}
@@ -352,7 +363,8 @@
/**
* Special layout transition that scales the clock view as its bounds change, to make it look
- * like the text is shrinking.
+ * like
+ * the text is shrinking.
*/
private class ClockBoundsTransition extends ChangeBounds {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index 016b8fe..4028109 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -191,32 +191,41 @@
mCurrentDialogArgs = args;
final int type = args.argi1;
+ // Create a new dialog but do not replace the current one yet.
+ BiometricDialogView newDialog;
if (type == BiometricAuthenticator.TYPE_FINGERPRINT) {
- mCurrentDialog = new FingerprintDialogView(mContext, mCallback);
+ newDialog = new FingerprintDialogView(mContext, mCallback);
} else if (type == BiometricAuthenticator.TYPE_FACE) {
- mCurrentDialog = new FaceDialogView(mContext, mCallback);
+ newDialog = new FaceDialogView(mContext, mCallback);
} else {
Log.e(TAG, "Unsupported type: " + type);
- }
-
- if (savedState != null) {
- mCurrentDialog.restoreState(savedState);
- }
-
- if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: "
- + mCurrentDialog.isAnimatingAway() + " type: " + type);
-
- if (mCurrentDialog.isAnimatingAway()) {
- mCurrentDialog.forceRemove();
- } else if (mDialogShowing) {
- Log.w(TAG, "Dialog already showing");
return;
}
+
+ if (DEBUG) Log.d(TAG, "handleShowDialog, "
+ + " savedState: " + savedState
+ + " mCurrentDialog: " + mCurrentDialog
+ + " newDialog: " + newDialog
+ + " type: " + type);
+
+ if (savedState != null) {
+ // SavedState is only non-null if it's from onConfigurationChanged. Restore the state
+ // even though it may be removed / re-created again
+ newDialog.restoreState(savedState);
+ } else if (mCurrentDialog != null && mDialogShowing) {
+ // If somehow we're asked to show a dialog, the old one doesn't need to be animated
+ // away. This can happen if the app cancels and re-starts auth during configuration
+ // change. This is ugly because we also have to do things on onConfigurationChanged
+ // here.
+ mCurrentDialog.forceRemove();
+ }
+
mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
- mCurrentDialog.setBundle((Bundle)args.arg1);
- mCurrentDialog.setRequireConfirmation((boolean) args.arg3);
- mCurrentDialog.setUserId(args.argi2);
- mCurrentDialog.setSkipIntro(skipAnimation);
+ newDialog.setBundle((Bundle) args.arg1);
+ newDialog.setRequireConfirmation((boolean) args.arg3);
+ newDialog.setUserId(args.argi2);
+ newDialog.setSkipIntro(skipAnimation);
+ mCurrentDialog = newDialog;
mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
mDialogShowing = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
index 7a42b03..1478a07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
@@ -20,7 +20,6 @@
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.SystemClock;
-import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -103,7 +102,6 @@
private void performBack() {
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
- mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
private boolean shouldExecuteBackOnUp() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index fcf5893..4c7fdb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -22,6 +22,7 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.os.SystemClock;
import android.util.FloatProperty;
import android.util.MathUtils;
import android.view.Gravity;
@@ -31,6 +32,7 @@
import android.view.WindowManager;
import com.android.systemui.R;
+import com.android.systemui.shared.system.QuickStepContract;
public class NavigationBarEdgePanel extends View {
private static final String TAG = "NavigationBarEdgePanel";
@@ -48,6 +50,7 @@
private static final float START_POINTING_RATIO = 0.3f;
private static final float POINTEDNESS_BEFORE_SNAP_RATIO = 0.4f;
private static final int ANIM_DURATION_MS = 150;
+ private static final long HAPTIC_TIMEOUT_MS = 200;
private final Paint mPaint = new Paint();
private final Paint mProtectionPaint = new Paint();
@@ -65,6 +68,8 @@
private float mStartY;
private float mStartX;
+ private boolean mDragSlopPassed;
+ private long mLastSlopHapticTime;
private boolean mGestureDetected;
private boolean mArrowsPointLeft;
private float mGestureLength;
@@ -169,6 +174,7 @@
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN : {
+ mDragSlopPassed = false;
show(event.getX(), event.getY());
break;
}
@@ -263,6 +269,13 @@
private void handleNewSwipePoint(float x) {
float dist = MathUtils.abs(x - mStartX);
+ // Apply a haptic on drag slop passed
+ if (!mDragSlopPassed && dist > QuickStepContract.getQuickStepDragSlopPx()) {
+ mDragSlopPassed = true;
+ performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+ mLastSlopHapticTime = SystemClock.uptimeMillis();
+ }
+
setDragProgress(MathUtils.constrainedMap(
0, 1.0f,
0, mGestureLength * TRACK_LENGTH_MULTIPLIER,
@@ -286,7 +299,10 @@
}
} else {
if (!mGestureDetected) {
- performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+ // Prevent another haptic if it was just used
+ if (SystemClock.uptimeMillis() - mLastSlopHapticTime > HAPTIC_TIMEOUT_MS) {
+ performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+ }
mGestureDetected = true;
mLegAnimator.setFloatValues(1f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index d404982..024404d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.KeyEvent.KEYCODE_UNKNOWN;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
@@ -101,7 +102,7 @@
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView,
defStyle, 0);
- mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
+ mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, KEYCODE_UNKNOWN);
mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
mPlaySounds = a.getBoolean(R.styleable.KeyButtonView_playSound, true);
@@ -124,7 +125,7 @@
@Override
public boolean isClickable() {
- return mCode != 0 || super.isClickable();
+ return mCode != KEYCODE_UNKNOWN || super.isClickable();
}
public void setCode(int code) {
@@ -163,7 +164,7 @@
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
- if (mCode != 0) {
+ if (mCode != KEYCODE_UNKNOWN) {
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, null));
if (mSupportsLongpress || isLongClickable()) {
info.addAction(
@@ -182,13 +183,13 @@
@Override
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
- if (action == ACTION_CLICK && mCode != 0) {
+ if (action == ACTION_CLICK && mCode != KEYCODE_UNKNOWN) {
sendEvent(KeyEvent.ACTION_DOWN, 0, SystemClock.uptimeMillis());
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
playSoundEffect(SoundEffectConstants.CLICK);
return true;
- } else if (action == ACTION_LONG_CLICK && mCode != 0) {
+ } else if (action == ACTION_LONG_CLICK && mCode != KEYCODE_UNKNOWN) {
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
@@ -197,6 +198,7 @@
return super.performAccessibilityActionInternal(action, arguments);
}
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
final boolean showSwipeUI = mOverviewProxyService.shouldShowSwipeUpUI();
final int action = ev.getAction();
@@ -218,7 +220,7 @@
// Use raw X and Y to detect gestures in case a parent changes the x and y values
mTouchDownX = (int) ev.getRawX();
mTouchDownY = (int) ev.getRawY();
- if (mCode != 0) {
+ if (mCode != KEYCODE_UNKNOWN) {
sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
} else {
// Provide the same haptic feedback that the system offers for virtual keys.
@@ -249,7 +251,7 @@
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
- if (mCode != 0) {
+ if (mCode != KEYCODE_UNKNOWN) {
sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
}
removeCallbacks(mCheckLongPress);
@@ -269,7 +271,7 @@
// and it feels weird to sometimes get a release haptic and other times not.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
}
- if (mCode != 0) {
+ if (mCode != KEYCODE_UNKNOWN) {
if (doIt) {
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
@@ -299,7 +301,7 @@
sendEvent(action, flags, SystemClock.uptimeMillis());
}
- void sendEvent(int action, int flags, long when) {
+ private void sendEvent(int action, int flags, long when) {
mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_NAV_BUTTON_EVENT)
.setType(MetricsEvent.TYPE_ACTION)
.setSubtype(mCode)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index b0d1106..29505a2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -101,6 +101,8 @@
// AND the plugin returns a view for the big clock
ClockPlugin plugin = mock(ClockPlugin.class);
when(plugin.getBigClockView()).thenReturn(mBigClock);
+ // AND in the keyguard state
+ mStateListener.onStateChanged(StatusBarState.KEYGUARD);
// WHEN the plugin is connected
mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
// THEN the big clock container is visible and it is the parent of the
@@ -166,6 +168,8 @@
ClockPlugin plugin = mock(ClockPlugin.class);
TextClock pluginView = new TextClock(getContext());
when(plugin.getBigClockView()).thenReturn(pluginView);
+ // AND in the keyguard state
+ mStateListener.onStateChanged(StatusBarState.KEYGUARD);
// WHEN the plugin is connected and then disconnected
mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
mKeyguardClockSwitch.getClockChangedListener().onClockChanged(null);
@@ -245,21 +249,25 @@
}
@Test
- public void onStateChanged_InvisibleInShade() {
+ public void onStateChanged_GoneInShade() {
// GIVEN that the big clock container is visible
mBigClockContainer.setVisibility(View.VISIBLE);
mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer);
// WHEN transitioned to SHADE state
mStateListener.onStateChanged(StatusBarState.SHADE);
- // THEN the container is invisible.
- assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.INVISIBLE);
+ // THEN the container is gone.
+ assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void onStateChanged_VisibleInKeyguard() {
- // GIVEN that the big clock container is invisible
- mBigClockContainer.setVisibility(View.INVISIBLE);
+ // GIVEN that the big clock container is gone
+ mBigClockContainer.setVisibility(View.GONE);
mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer);
+ // AND GIVEN that a plugin is active.
+ ClockPlugin plugin = mock(ClockPlugin.class);
+ when(plugin.getBigClockView()).thenReturn(mBigClock);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
// WHEN transitioned to KEYGUARD state
mStateListener.onStateChanged(StatusBarState.KEYGUARD);
// THEN the container is visible.
@@ -274,6 +282,8 @@
ClockPlugin plugin = mock(ClockPlugin.class);
when(plugin.getBigClockView()).thenReturn(mBigClock);
mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
+ // AND in the keyguard state
+ mStateListener.onStateChanged(StatusBarState.KEYGUARD);
// WHEN the container is associated with the clock switch
mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer);
// THEN the container remains visible.
@@ -281,20 +291,6 @@
}
@Test
- public void setBigClockContainer_invisible() {
- // GIVEN that the big clock container is invisible
- mBigClockContainer.setVisibility(View.INVISIBLE);
- // AND GIVEN that a plugin is active.
- ClockPlugin plugin = mock(ClockPlugin.class);
- when(plugin.getBigClockView()).thenReturn(mBigClock);
- mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
- // WHEN the container is associated with the clock switch
- mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer);
- // THEN the container remains invisible.
- assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.INVISIBLE);
- }
-
- @Test
public void setBigClockContainer_gone() {
// GIVEN that the big clock container is gone
mBigClockContainer.setVisibility(View.GONE);
@@ -302,6 +298,8 @@
ClockPlugin plugin = mock(ClockPlugin.class);
when(plugin.getBigClockView()).thenReturn(mBigClock);
mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
+ // AND in the keyguard state
+ mStateListener.onStateChanged(StatusBarState.KEYGUARD);
// WHEN the container is associated with the clock switch
mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer);
// THEN the container is made visible.
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 65e31f3..1e3f20e 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -809,13 +809,9 @@
// Announce the end of the gesture recognition.
sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
+ // Don't announce the end of a the touch interaction if users didn't lift their fingers.
if (interactionEnd) {
- // Announce the end of a the touch interaction.
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
- } else {
- // If gesture detection is end, but user doesn't release the finger, announce the
- // transition to exploration state.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
}
mExitGestureDetectionModeDelayed.cancel();
@@ -1151,10 +1147,7 @@
public void run() {
// Announce the end of gesture recognition.
sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
- // Clearing puts is in touch exploration state with a finger already
- // down, so announce the transition to exploration state.
clear();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index c2a0611..7fab2b96 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -501,6 +501,8 @@
}
if (broadcast) {
+ // needs to be sent to everyone because we don't know which user may have changed
+ // LOCATION_MODE state.
mContext.sendBroadcastAsUser(
new Intent(LocationManager.MODE_CHANGED_ACTION),
UserHandle.ALL);
@@ -1212,6 +1214,13 @@
"-" + mName,
mCurrentUserId);
}
+
+ // needs to be sent to all users because whether or not a provider is enabled for
+ // a given user is complicated... we broadcast to everyone and let them figure it
+ // out via isProviderEnabled()
+ Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION);
+ intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
if (useable == mUseable) {
@@ -1232,10 +1241,6 @@
}
updateProviderUseableLocked(this);
-
- mContext.sendBroadcastAsUser(
- new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
- UserHandle.ALL);
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7c6049c..8225476 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5492,6 +5492,12 @@
}
}
+ private boolean isAppBad(ApplicationInfo info) {
+ synchronized (this) {
+ return mAppErrors.isBadProcessLocked(info);
+ }
+ }
+
// NOTE: this is an internal method used by the OnShellCommand implementation only and should
// be guarded by permission checking.
int getUidState(int uid) {
@@ -18078,6 +18084,11 @@
}
@Override
+ public boolean isAppBad(ApplicationInfo info) {
+ return ActivityManagerService.this.isAppBad(info);
+ }
+
+ @Override
public void clearPendingBackup(int userId) {
ActivityManagerService.this.clearPendingBackup(userId);
}
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 7da848c..4e0380d 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -59,6 +59,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
+import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
@@ -103,6 +104,7 @@
@Override
public void onStart() {
+ publishBinderService(Context.ATTENTION_SERVICE, new BinderService());
publishLocalService(AttentionManagerInternal.class, new LocalService());
}
@@ -329,17 +331,15 @@
return null;
}
- private void dumpInternal(PrintWriter pw) {
- if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println("Attention Manager Service (dumpsys attention)\n");
+ private void dumpInternal(IndentingPrintWriter ipw) {
+ ipw.println("Attention Manager Service (dumpsys attention) state:\n");
ipw.printPair("context", mContext);
- pw.println();
+ ipw.println();
synchronized (mLock) {
int size = mUserStates.size();
ipw.print("Number user states: ");
- pw.println(size);
+ ipw.println(size);
if (size > 0) {
ipw.increaseIndent();
for (int i = 0; i < size; i++) {
@@ -591,4 +591,15 @@
}
}
}
+
+ private final class BinderService extends Binder {
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) {
+ return;
+ }
+
+ dumpInternal(new IndentingPrintWriter(pw, " "));
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index ddd416e..f313e1d 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -57,6 +57,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -68,6 +69,7 @@
import android.util.StatsLog;
import com.android.internal.R;
+import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
@@ -85,22 +87,137 @@
private static final String TAG = "BiometricService";
+ private static final int MSG_ON_TASK_STACK_CHANGED = 1;
+ private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
+ private static final int MSG_ON_AUTHENTICATION_FAILED = 3;
+ private static final int MSG_ON_ERROR = 4;
+ private static final int MSG_ON_ACQUIRED = 5;
+ private static final int MSG_ON_DISMISSED = 6;
+ private static final int MSG_ON_TRY_AGAIN_PRESSED = 7;
+ private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8;
+ private static final int MSG_AUTHENTICATE = 9;
+ private static final int MSG_CANCEL_AUTHENTICATION = 10;
+
private static final int[] FEATURE_ID = {
TYPE_FINGERPRINT,
TYPE_IRIS,
TYPE_FACE
};
+ /**
+ * Authentication either just called and we have not transitioned to the CALLED state, or
+ * authentication terminated (success or error).
+ */
+ private static final int STATE_AUTH_IDLE = 0;
+ /**
+ * Authentication was called and we are waiting for the <Biometric>Services to return their
+ * cookies before starting the hardware and showing the BiometricPrompt.
+ */
+ private static final int STATE_AUTH_CALLED = 1;
+ /**
+ * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
+ */
+ private static final int STATE_AUTH_STARTED = 2;
+ /**
+ * Authentication is paused, waiting for the user to press "try again" button. Only
+ * passive modalities such as Face or Iris should have this state. Note that for passive
+ * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
+ * fingerprint.
+ */
+ private static final int STATE_AUTH_PAUSED = 3;
+ /**
+ * Authentication is successful, but we're waiting for the user to press "confirm" button.
+ */
+ private static final int STATE_AUTH_PENDING_CONFIRM = 5;
+
+ private final class AuthSession {
+ // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
+ // <Biometric>Services before we can start authenticating. Pairs that have been returned
+ // are moved to mModalitiesMatched.
+ final HashMap<Integer, Integer> mModalitiesWaiting;
+ // Pairs that have been matched.
+ final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>();
+
+ // The following variables are passed to authenticateInternal, which initiates the
+ // appropriate <Biometric>Services.
+ final IBinder mToken;
+ final long mSessionId;
+ final int mUserId;
+ // Original receiver from BiometricPrompt.
+ final IBiometricServiceReceiver mClientReceiver;
+ final String mOpPackageName;
+ // Info to be shown on BiometricDialog when all cookies are returned.
+ final Bundle mBundle;
+ final int mCallingUid;
+ final int mCallingPid;
+ final int mCallingUserId;
+ // Continue authentication with the same modality/modalities after "try again" is
+ // pressed
+ final int mModality;
+ final boolean mRequireConfirmation;
+
+ // The current state, which can be either idle, called, or started
+ private int mState = STATE_AUTH_IDLE;
+ // For explicit confirmation, do not send to keystore until the user has confirmed
+ // the authentication.
+ byte[] mTokenEscrow;
+
+ // Timestamp when hardware authentication occurred
+ private long mAuthenticatedTimeMs;
+
+ AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
+ int userId, IBiometricServiceReceiver receiver, String opPackageName,
+ Bundle bundle, int callingUid, int callingPid, int callingUserId,
+ int modality, boolean requireConfirmation) {
+ mModalitiesWaiting = modalities;
+ mToken = token;
+ mSessionId = sessionId;
+ mUserId = userId;
+ mClientReceiver = receiver;
+ mOpPackageName = opPackageName;
+ mBundle = bundle;
+ mCallingUid = callingUid;
+ mCallingPid = callingPid;
+ mCallingUserId = callingUserId;
+ mModality = modality;
+ mRequireConfirmation = requireConfirmation;
+ }
+
+ boolean isCrypto() {
+ return mSessionId != 0;
+ }
+
+ boolean containsCookie(int cookie) {
+ if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
+ return true;
+ }
+ if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private final class BiometricTaskStackListener extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ mHandler.sendEmptyMessage(MSG_ON_TASK_STACK_CHANGED);
+ }
+ }
+
private final AppOpsManager mAppOps;
- private final Handler mHandler;
private final boolean mHasFeatureFingerprint;
private final boolean mHasFeatureIris;
private final boolean mHasFeatureFace;
private final SettingObserver mSettingObserver;
private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
+ private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
+ private final Random mRandom = new Random();
private IFingerprintService mFingerprintService;
private IFaceService mFaceService;
+ private IActivityTaskManager mActivityTaskManager;
+ private IStatusBarService mStatusBarService;
// Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
// polymorphism :/
@@ -113,6 +230,108 @@
// should be safe.
private int mCurrentModality;
+ // The current authentication session, null if idle/done. We need to track both the current
+ // and pending sessions since errors may be sent to either.
+ private AuthSession mCurrentAuthSession;
+ private AuthSession mPendingAuthSession;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ON_TASK_STACK_CHANGED: {
+ handleTaskStackChanged();
+ break;
+ }
+
+ case MSG_ON_AUTHENTICATION_SUCCEEDED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleAuthenticationSucceeded(
+ (boolean) args.arg1 /* requireConfirmation */,
+ (byte[]) args.arg2 /* token */);
+ args.recycle();
+ break;
+ }
+
+ case MSG_ON_AUTHENTICATION_FAILED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleAuthenticationFailed(
+ args.argi1 /* cookie */,
+ (boolean) args.arg1 /* requireConfirmation */);
+ args.recycle();
+ break;
+ }
+
+ case MSG_ON_ERROR: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleOnError(
+ args.argi1 /* cookie */,
+ args.argi2 /* error */,
+ (String) args.arg1 /* message */);
+ args.recycle();
+ break;
+ }
+
+ case MSG_ON_ACQUIRED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleOnAcquired(
+ args.argi1 /* acquiredInfo */,
+ (String) args.arg1 /* message */);
+ args.recycle();
+ break;
+ }
+
+ case MSG_ON_DISMISSED: {
+ handleOnDismissed(msg.arg1);
+ break;
+ }
+
+ case MSG_ON_TRY_AGAIN_PRESSED: {
+ handleOnTryAgainPressed();
+ break;
+ }
+
+ case MSG_ON_READY_FOR_AUTHENTICATION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleOnReadyForAuthentication(
+ args.argi1 /* cookie */,
+ (boolean) args.arg1 /* requireConfirmation */,
+ args.argi2 /* userId */);
+ args.recycle();
+ break;
+ }
+
+ case MSG_AUTHENTICATE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleAuthenticate(
+ (IBinder) args.arg1 /* token */,
+ (long) args.arg2 /* sessionId */,
+ args.argi1 /* userid */,
+ (IBiometricServiceReceiver) args.arg3 /* receiver */,
+ (String) args.arg4 /* opPackageName */,
+ (Bundle) args.arg5 /* bundle */,
+ args.argi2 /* callingUid */,
+ args.argi3 /* callingPid */,
+ args.argi4 /* callingUserId */);
+ args.recycle();
+ break;
+ }
+
+ case MSG_CANCEL_AUTHENTICATION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleCancelAuthentication(
+ (IBinder) args.arg1 /* token */,
+ (String) args.arg2 /* opPackageName */);
+ args.recycle();
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ };
+
private final class Authenticator {
int mType;
BiometricAuthenticator mAuthenticator;
@@ -251,142 +470,62 @@
}
}
+ // Wrap the client's receiver so we can do things with the BiometricDialog first
+ private final IBiometricServiceReceiverInternal mInternalReceiver =
+ new IBiometricServiceReceiverInternal.Stub() {
+ @Override
+ public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
+ throws RemoteException {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = requireConfirmation;
+ args.arg2 = token;
+ mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget();
+ }
+
+ @Override
+ public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
+ throws RemoteException {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = cookie;
+ args.arg1 = requireConfirmation;
+ mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, args).sendToTarget();
+ }
+
+ @Override
+ public void onError(int cookie, int error, String message) throws RemoteException {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = cookie;
+ args.argi2 = error;
+ args.arg1 = message;
+ mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget();
+ }
+
+ @Override
+ public void onAcquired(int acquiredInfo, String message) throws RemoteException {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = acquiredInfo;
+ args.arg1 = message;
+ mHandler.obtainMessage(MSG_ON_ACQUIRED, args).sendToTarget();
+ }
+
+ @Override
+ public void onDialogDismissed(int reason) throws RemoteException {
+ mHandler.obtainMessage(MSG_ON_DISMISSED, reason, 0 /* arg2 */).sendToTarget();
+ }
+
+ @Override
+ public void onTryAgainPressed() {
+ mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED);
+ }
+ };
+
+
/**
* This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
* should not carry any state. The reality is we need to keep a tiny amount of state so that
* cancelAuthentication() can go to the right place.
*/
private final class BiometricServiceWrapper extends IBiometricService.Stub {
-
- /**
- * Authentication either just called and we have not transitioned to the CALLED state, or
- * authentication terminated (success or error).
- */
- private static final int STATE_AUTH_IDLE = 0;
- /**
- * Authentication was called and we are waiting for the <Biometric>Services to return their
- * cookies before starting the hardware and showing the BiometricPrompt.
- */
- private static final int STATE_AUTH_CALLED = 1;
- /**
- * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
- */
- private static final int STATE_AUTH_STARTED = 2;
- /**
- * Authentication is paused, waiting for the user to press "try again" button. Only
- * passive modalities such as Face or Iris should have this state. Note that for passive
- * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
- * fingerprint.
- */
- private static final int STATE_AUTH_PAUSED = 3;
- /**
- * Authentication is successful, but we're waiting for the user to press "confirm" button.
- */
- private static final int STATE_AUTH_PENDING_CONFIRM = 5;
-
- final class AuthSession {
- // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
- // <Biometric>Services before we can start authenticating. Pairs that have been returned
- // are moved to mModalitiesMatched.
- final HashMap<Integer, Integer> mModalitiesWaiting;
- // Pairs that have been matched.
- final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>();
-
- // The following variables are passed to authenticateInternal, which initiates the
- // appropriate <Biometric>Services.
- final IBinder mToken;
- final long mSessionId;
- final int mUserId;
- // Original receiver from BiometricPrompt.
- final IBiometricServiceReceiver mClientReceiver;
- final String mOpPackageName;
- // Info to be shown on BiometricDialog when all cookies are returned.
- final Bundle mBundle;
- final int mCallingUid;
- final int mCallingPid;
- final int mCallingUserId;
- // Continue authentication with the same modality/modalities after "try again" is
- // pressed
- final int mModality;
- final boolean mRequireConfirmation;
-
- // The current state, which can be either idle, called, or started
- private int mState = STATE_AUTH_IDLE;
- // For explicit confirmation, do not send to keystore until the user has confirmed
- // the authentication.
- byte[] mTokenEscrow;
-
- // Timestamp when hardware authentication occurred
- private long mAuthenticatedTimeMs;
-
- AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
- int userId, IBiometricServiceReceiver receiver, String opPackageName,
- Bundle bundle, int callingUid, int callingPid, int callingUserId,
- int modality, boolean requireConfirmation) {
- mModalitiesWaiting = modalities;
- mToken = token;
- mSessionId = sessionId;
- mUserId = userId;
- mClientReceiver = receiver;
- mOpPackageName = opPackageName;
- mBundle = bundle;
- mCallingUid = callingUid;
- mCallingPid = callingPid;
- mCallingUserId = callingUserId;
- mModality = modality;
- mRequireConfirmation = requireConfirmation;
- }
-
- boolean isCrypto() {
- return mSessionId != 0;
- }
-
- boolean containsCookie(int cookie) {
- if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
- return true;
- }
- if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) {
- return true;
- }
- return false;
- }
- }
-
- final class BiometricTaskStackListener extends TaskStackListener {
- @Override
- public void onTaskStackChanged() {
- try {
- final List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (mCurrentAuthSession != null
- && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) {
- mStatusBarService.hideBiometricDialog();
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- mCurrentAuthSession.mClientReceiver.onError(
- BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- getContext().getString(
- com.android.internal.R.string.biometric_error_canceled)
- );
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- }
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to get running tasks", e);
- }
- }
- }
-
- private final IActivityTaskManager mActivityTaskManager = getContext().getSystemService(
- ActivityTaskManager.class).getService();
- private final IStatusBarService mStatusBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- private final BiometricTaskStackListener mTaskStackListener =
- new BiometricTaskStackListener();
- private final Random mRandom = new Random();
-
// TODO(b/123378871): Remove when moved.
// When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the
// client (app) receiver. BiometricService internally launches CDCA which invokes
@@ -395,331 +534,15 @@
// to this receiver.
private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver;
- // The current authentication session, null if idle/done. We need to track both the current
- // and pending sessions since errors may be sent to either.
- private AuthSession mCurrentAuthSession;
- private AuthSession mPendingAuthSession;
-
- // Wrap the client's receiver so we can do things with the BiometricDialog first
- private final IBiometricServiceReceiverInternal mInternalReceiver =
- new IBiometricServiceReceiverInternal.Stub() {
- @Override
- public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
- throws RemoteException {
- try {
- // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
- // after user dismissed/canceled dialog).
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null");
- return;
- }
-
- if (!requireConfirmation) {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- KeyStore.getInstance().addAuthToken(token);
- mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- } else {
- mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
- // Store the auth token and submit it to keystore after the confirmation
- // button has been pressed.
- mCurrentAuthSession.mTokenEscrow = token;
- mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
- }
-
- // Notify SysUI that the biometric has been authenticated. SysUI already knows
- // the implicit/explicit state and will react accordingly.
- mStatusBarService.onBiometricAuthenticated(true);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
-
- @Override
- public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
- throws RemoteException {
- try {
- // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
- // after user dismissed/canceled dialog).
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "onAuthenticationFailed(): Auth session is null");
- return;
- }
-
- mStatusBarService.onBiometricAuthenticated(false);
-
- // TODO: This logic will need to be updated if BP is multi-modal
- if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
- // Pause authentication. onBiometricAuthenticated(false) causes the
- // dialog to show a "try again" button for passive modalities.
- mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
- }
-
- mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
-
- @Override
- public void onError(int cookie, int error, String message) throws RemoteException {
- Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
- // Errors can either be from the current auth session or the pending auth session.
- // The pending auth session may receive errors such as ERROR_LOCKOUT before
- // it becomes the current auth session. Similarly, the current auth session may
- // receive errors such as ERROR_CANCELED while the pending auth session is preparing
- // to be started. Thus we must match error messages with their cookies to be sure
- // of their intended receivers.
- try {
- if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
- if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
- mStatusBarService.onBiometricError(message);
- if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
- mActivityTaskManager.unregisterTaskStackListener(
- mTaskStackListener);
- mCurrentAuthSession.mClientReceiver.onError(error, message);
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- mStatusBarService.hideBiometricDialog();
- } else {
- // Send errors after the dialog is dismissed.
- mHandler.postDelayed(() -> {
- try {
- if (mCurrentAuthSession != null) {
- mActivityTaskManager.unregisterTaskStackListener(
- mTaskStackListener);
- mCurrentAuthSession.mClientReceiver.onError(error,
- message);
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }, BiometricPrompt.HIDE_DIALOG_DELAY);
- }
- } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
- // In the "try again" state, we should forward canceled errors to
- // the client and and clean up.
- mCurrentAuthSession.mClientReceiver.onError(error, message);
- mStatusBarService.onBiometricError(message);
- mActivityTaskManager.unregisterTaskStackListener(
- mTaskStackListener);
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- } else {
- Slog.e(TAG, "Impossible session error state: "
- + mCurrentAuthSession.mState);
- }
- } else if (mPendingAuthSession != null
- && mPendingAuthSession.containsCookie(cookie)) {
- if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
- mPendingAuthSession.mClientReceiver.onError(error, message);
- mPendingAuthSession.mState = STATE_AUTH_IDLE;
- mPendingAuthSession = null;
- } else {
- Slog.e(TAG, "Impossible pending session error state: "
- + mPendingAuthSession.mState);
- }
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
-
- @Override
- public void onAcquired(int acquiredInfo, String message) throws RemoteException {
- // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
- // after user dismissed/canceled dialog).
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "onAcquired(): Auth session is null");
- return;
- }
-
- if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- try {
- mStatusBarService.onBiometricHelp(message);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
- }
-
- @Override
- public void onDialogDismissed(int reason) throws RemoteException {
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null");
- return;
- }
-
- logDialogDismissed(reason);
-
- if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Positive button is used by passive modalities as a "confirm" button,
- // do not send to client
- mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
- // Cancel authentication. Skip the token/package check since we are cancelling
- // from system server. The interface is permission protected so this is fine.
- cancelInternal(null /* token */, null /* package */, false /* fromClient */);
- }
- if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
- mCurrentAuthSession.mClientReceiver.onError(
- BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
- getContext().getString(
- com.android.internal.R.string.biometric_error_user_canceled));
- } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Have the service send the token to KeyStore, and send onAuthenticated
- // to the application
- KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
- mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
- }
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- }
-
- @Override
- public void onTryAgainPressed() {
- Slog.d(TAG, "onTryAgainPressed");
- // No need to check permission, since it can only be invoked by SystemUI
- // (or system server itself).
- mHandler.post(() -> {
- authenticateInternal(mCurrentAuthSession.mToken,
- mCurrentAuthSession.mSessionId,
- mCurrentAuthSession.mUserId,
- mCurrentAuthSession.mClientReceiver,
- mCurrentAuthSession.mOpPackageName,
- mCurrentAuthSession.mBundle,
- mCurrentAuthSession.mCallingUid,
- mCurrentAuthSession.mCallingPid,
- mCurrentAuthSession.mCallingUserId,
- mCurrentAuthSession.mModality);
- });
- }
-
- private void logDialogDismissed(int reason) {
- if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Explicit auth, authentication confirmed.
- // Latency in this case is authenticated -> confirmed. <Biometric>Service
- // should have the first half (first acquired -> authenticated).
- final long latency = System.currentTimeMillis()
- - mCurrentAuthSession.mAuthenticatedTimeMs;
-
- if (LoggableMonitor.DEBUG) {
- Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality()
- + ", User: " + mCurrentAuthSession.mUserId
- + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
- + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
- + ", RequireConfirmation: "
- + mCurrentAuthSession.mRequireConfirmation
- + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
- + ", Latency: " + latency);
- }
-
- StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
- statsModality(),
- mCurrentAuthSession.mUserId,
- mCurrentAuthSession.isCrypto(),
- BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
- mCurrentAuthSession.mRequireConfirmation,
- StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
- latency);
- } else {
- int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE
- ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON
- : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL
- ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
- : 0;
- if (LoggableMonitor.DEBUG) {
- Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality()
- + ", User: " + mCurrentAuthSession.mUserId
- + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
- + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE
- + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
- + ", Error: " + error);
- }
- // Auth canceled
- StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
- statsModality(),
- mCurrentAuthSession.mUserId,
- mCurrentAuthSession.isCrypto(),
- BiometricsProtoEnums.ACTION_AUTHENTICATE,
- BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
- error,
- 0 /* vendorCode */);
- }
- }
-
- private int statsModality() {
- int modality = 0;
- if (mCurrentAuthSession == null) {
- return BiometricsProtoEnums.MODALITY_UNKNOWN;
- }
- if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT)
- != 0) {
- modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT;
- }
- if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) {
- modality |= BiometricsProtoEnums.MODALITY_IRIS;
- }
- if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) {
- modality |= BiometricsProtoEnums.MODALITY_FACE;
- }
- return modality;
- }
- };
-
@Override // Binder call
public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) {
checkInternalPermission();
- Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
- if (pair.getValue() == cookie) {
- mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue());
- mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey());
- Slog.d(TAG, "Matched cookie: " + cookie + ", "
- + mPendingAuthSession.mModalitiesWaiting.size() + " remaining");
- break;
- }
- }
-
- if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
- final boolean continuing = mCurrentAuthSession != null &&
- (mCurrentAuthSession.mState == STATE_AUTH_PAUSED);
-
- mCurrentAuthSession = mPendingAuthSession;
- mPendingAuthSession = null;
-
- mCurrentAuthSession.mState = STATE_AUTH_STARTED;
- try {
- int modality = TYPE_NONE;
- it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
- if (pair.getKey() == TYPE_FINGERPRINT) {
- mFingerprintService.startPreparedClient(pair.getValue());
- } else if (pair.getKey() == TYPE_IRIS) {
- Slog.e(TAG, "Iris unsupported");
- } else if (pair.getKey() == TYPE_FACE) {
- mFaceService.startPreparedClient(pair.getValue());
- } else {
- Slog.e(TAG, "Unknown modality: " + pair.getKey());
- }
- modality |= pair.getKey();
- }
-
- if (!continuing) {
- mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
- mInternalReceiver, modality, requireConfirmation, userId);
- mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = cookie;
+ args.arg1 = requireConfirmation;
+ args.argi2 = userId;
+ mHandler.obtainMessage(MSG_ON_READY_FOR_AUTHENTICATION, args).sendToTarget();
}
@Override // Binder call
@@ -754,25 +577,23 @@
checkInternalPermission();
// Set the default title if necessary
try {
- if (useDefaultTitle) {
- final List<ActivityManager.RunningAppProcessInfo> procs =
- ActivityManager.getService().getRunningAppProcesses();
- for (int i = 0; i < procs.size(); i++) {
- final ActivityManager.RunningAppProcessInfo info = procs.get(i);
- if (info.uid == callingUid
- && info.importance == IMPORTANCE_FOREGROUND) {
- PackageManager pm = getContext().getPackageManager();
- final CharSequence label = pm.getApplicationLabel(
- pm.getApplicationInfo(info.processName,
- PackageManager.GET_META_DATA));
- final String title = getContext()
- .getString(R.string.biometric_dialog_default_title, label);
- if (TextUtils.isEmpty(
- bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
- bundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
- }
- break;
+ final List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ for (int i = 0; i < procs.size(); i++) {
+ final ActivityManager.RunningAppProcessInfo info = procs.get(i);
+ if (info.uid == callingUid
+ && info.importance == IMPORTANCE_FOREGROUND) {
+ PackageManager pm = getContext().getPackageManager();
+ final CharSequence label = pm.getApplicationLabel(
+ pm.getApplicationInfo(info.processName,
+ PackageManager.GET_META_DATA));
+ final String title = getContext()
+ .getString(R.string.biometric_dialog_default_title, label);
+ if (TextUtils.isEmpty(
+ bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
+ bundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
}
+ break;
}
}
} catch (RemoteException e) {
@@ -792,7 +613,8 @@
KeyguardManager.class);
if (!kgm.isDeviceSecure()) {
try {
- receiver.onError(BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
+ receiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
getContext().getString(
R.string.biometric_error_device_not_secured));
} catch (RemoteException e) {
@@ -811,160 +633,63 @@
return;
}
- mHandler.post(() -> {
- final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
- final int modality = result.first;
- final int error = result.second;
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = token;
+ args.arg2 = sessionId;
+ args.argi1 = userId;
+ args.arg3 = receiver;
+ args.arg4 = opPackageName;
+ args.arg5 = bundle;
+ args.argi2 = callingUid;
+ args.argi3 = callingPid;
+ args.argi4 = callingUserId;
- // Check for errors, notify callback, and return
- if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
- try {
- final String hardwareUnavailable =
- getContext().getString(R.string.biometric_error_hw_unavailable);
- switch (error) {
- case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
- receiver.onError(error, hardwareUnavailable);
- break;
- case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
- receiver.onError(error, hardwareUnavailable);
- break;
- case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
- receiver.onError(error,
- getErrorString(modality, error, 0 /* vendorCode */));
- break;
- default:
- Slog.e(TAG, "Unhandled error");
- break;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- return;
- }
-
- mCurrentModality = modality;
-
- // Start preparing for authentication. Authentication starts when
- // all modalities requested have invoked onReadyForAuthentication.
- authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
- callingUid, callingPid, callingUserId, modality);
- });
+ mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
}
@Override // Binder call
public void onConfirmDeviceCredentialSuccess() {
checkInternalPermission();
- if (mConfirmDeviceCredentialReceiver == null) {
- Slog.w(TAG, "onCDCASuccess null!");
- return;
- }
- try {
- mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException", e);
- }
- mConfirmDeviceCredentialReceiver = null;
+ mHandler.post(() -> {
+ if (mConfirmDeviceCredentialReceiver == null) {
+ Slog.w(TAG, "onCDCASuccess null!");
+ return;
+ }
+ try {
+ mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException", e);
+ }
+ mConfirmDeviceCredentialReceiver = null;
+ });
}
@Override // Binder call
public void onConfirmDeviceCredentialError(int error, String message) {
checkInternalPermission();
- if (mConfirmDeviceCredentialReceiver == null) {
- Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message);
- return;
- }
- try {
- mConfirmDeviceCredentialReceiver.onError(error, message);
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException", e);
- }
- mConfirmDeviceCredentialReceiver = null;
- }
-
- /**
- * authenticate() (above) which is called from BiometricPrompt determines which
- * modality/modalities to start authenticating with. authenticateInternal() should only be
- * used for:
- * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
- * invoked, shortly after which BiometricPrompt is shown and authentication starts
- * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
- * and the user has pressed "try again"
- */
- private void authenticateInternal(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
- int callingUid, int callingPid, int callingUserId, int modality) {
- try {
- boolean requireConfirmation = bundle.getBoolean(
- BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
- if ((modality & TYPE_FACE) != 0) {
- // Check if the user has forced confirmation to be required in Settings.
- requireConfirmation = requireConfirmation
- || mSettingObserver.getFaceAlwaysRequireConfirmation();
+ mHandler.post(() -> {
+ if (mConfirmDeviceCredentialReceiver == null) {
+ Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message);
+ return;
}
- // Generate random cookies to pass to the services that should prepare to start
- // authenticating. Store the cookie here and wait for all services to "ack"
- // with the cookie. Once all cookies are received, we can show the prompt
- // and let the services start authenticating. The cookie should be non-zero.
- final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
- Slog.d(TAG, "Creating auth session. Modality: " + modality
- + ", cookie: " + cookie);
- final HashMap<Integer, Integer> authenticators = new HashMap<>();
- authenticators.put(modality, cookie);
- mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
- receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
- modality, requireConfirmation);
- mPendingAuthSession.mState = STATE_AUTH_CALLED;
- // No polymorphism :(
- if ((modality & TYPE_FINGERPRINT) != 0) {
- mFingerprintService.prepareForAuthentication(token, sessionId, userId,
- mInternalReceiver, opPackageName, cookie,
- callingUid, callingPid, callingUserId);
+ try {
+ mConfirmDeviceCredentialReceiver.onError(error, message);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException", e);
}
- if ((modality & TYPE_IRIS) != 0) {
- Slog.w(TAG, "Iris unsupported");
- }
- if ((modality & TYPE_FACE) != 0) {
- mFaceService.prepareForAuthentication(requireConfirmation,
- token, sessionId, userId, mInternalReceiver, opPackageName,
- cookie, callingUid, callingPid, callingUserId);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to start authentication", e);
- }
+ mConfirmDeviceCredentialReceiver = null;
+ });
}
@Override // Binder call
public void cancelAuthentication(IBinder token, String opPackageName)
throws RemoteException {
checkPermission();
- if (token == null || opPackageName == null) {
- Slog.e(TAG, "Unable to cancel, one or more null arguments");
- return;
- }
- // We need to check the current authenticators state. If we're pending confirm
- // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
- // since we won't be getting an onError from the driver.
- if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
- mHandler.post(() -> {
- try {
- // Send error to client
- mCurrentAuthSession.mClientReceiver.onError(
- BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- getContext().getString(
- com.android.internal.R.string.biometric_error_user_canceled)
- );
-
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- mStatusBarService.hideBiometricDialog();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- });
- } else {
- cancelInternal(token, opPackageName, true /* fromClient */);
- }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = token;
+ args.arg2 = opPackageName;
+ mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget();
}
@Override // Binder call
@@ -1027,31 +752,6 @@
Binder.restoreCallingIdentity(ident);
}
}
-
- void cancelInternal(IBinder token, String opPackageName, boolean fromClient) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
- mHandler.post(() -> {
- try {
- // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
- // drivers have canceled authentication.
- if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
- mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId, fromClient);
- }
- if ((mCurrentModality & TYPE_IRIS) != 0) {
- Slog.w(TAG, "Iris unsupported");
- }
- if ((mCurrentModality & TYPE_FACE) != 0) {
- mFaceService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId, fromClient);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to cancel authentication");
- }
- });
- }
}
private void checkAppOp(String opPackageName, int callingUid) {
@@ -1088,7 +788,6 @@
super(context);
mAppOps = context.getSystemService(AppOpsManager.class);
- mHandler = new Handler(Looper.getMainLooper());
mEnabledOnKeyguardCallbacks = new ArrayList<>();
mSettingObserver = new SettingObserver(mHandler);
@@ -1123,6 +822,10 @@
ServiceManager.getService(Context.FACE_SERVICE));
}
+ mActivityTaskManager = ActivityTaskManager.getService();
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+
// Cache the authenticators
for (int i = 0; i < FEATURE_ID.length; i++) {
if (hasFeature(FEATURE_ID[i])) {
@@ -1259,4 +962,491 @@
return false;
}
}
+
+ private void logDialogDismissed(int reason) {
+ if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Explicit auth, authentication confirmed.
+ // Latency in this case is authenticated -> confirmed. <Biometric>Service
+ // should have the first half (first acquired -> authenticated).
+ final long latency = System.currentTimeMillis()
+ - mCurrentAuthSession.mAuthenticatedTimeMs;
+
+ if (LoggableMonitor.DEBUG) {
+ Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality()
+ + ", User: " + mCurrentAuthSession.mUserId
+ + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
+ + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
+ + ", RequireConfirmation: "
+ + mCurrentAuthSession.mRequireConfirmation
+ + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
+ + ", Latency: " + latency);
+ }
+
+ StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
+ statsModality(),
+ mCurrentAuthSession.mUserId,
+ mCurrentAuthSession.isCrypto(),
+ BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+ mCurrentAuthSession.mRequireConfirmation,
+ StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
+ latency);
+ } else {
+ int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE
+ ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON
+ : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL
+ ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
+ : 0;
+ if (LoggableMonitor.DEBUG) {
+ Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality()
+ + ", User: " + mCurrentAuthSession.mUserId
+ + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
+ + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE
+ + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
+ + ", Error: " + error);
+ }
+ // Auth canceled
+ StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
+ statsModality(),
+ mCurrentAuthSession.mUserId,
+ mCurrentAuthSession.isCrypto(),
+ BiometricsProtoEnums.ACTION_AUTHENTICATE,
+ BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+ error,
+ 0 /* vendorCode */);
+ }
+ }
+
+ private int statsModality() {
+ int modality = 0;
+ if (mCurrentAuthSession == null) {
+ return BiometricsProtoEnums.MODALITY_UNKNOWN;
+ }
+ if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT)
+ != 0) {
+ modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT;
+ }
+ if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) {
+ modality |= BiometricsProtoEnums.MODALITY_IRIS;
+ }
+ if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) {
+ modality |= BiometricsProtoEnums.MODALITY_FACE;
+ }
+ return modality;
+ }
+
+ private void handleTaskStackChanged() {
+ try {
+ final List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (!runningTasks.isEmpty()) {
+ final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+ if (mCurrentAuthSession != null
+ && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) {
+ mStatusBarService.hideBiometricDialog();
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_canceled)
+ );
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to get running tasks", e);
+ }
+ }
+
+ private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
+
+ try {
+ // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+ // after user dismissed/canceled dialog).
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null");
+ return;
+ }
+
+ if (!requireConfirmation) {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ KeyStore.getInstance().addAuthToken(token);
+ mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ } else {
+ mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
+ // Store the auth token and submit it to keystore after the confirmation
+ // button has been pressed.
+ mCurrentAuthSession.mTokenEscrow = token;
+ mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
+ }
+
+ // Notify SysUI that the biometric has been authenticated. SysUI already knows
+ // the implicit/explicit state and will react accordingly.
+ mStatusBarService.onBiometricAuthenticated(true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ private void handleAuthenticationFailed(int cookie, boolean requireConfirmation) {
+ try {
+ // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+ // after user dismissed/canceled dialog).
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "onAuthenticationFailed(): Auth session is null");
+ return;
+ }
+
+ mStatusBarService.onBiometricAuthenticated(false);
+
+ // TODO: This logic will need to be updated if BP is multi-modal
+ if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
+ // Pause authentication. onBiometricAuthenticated(false) causes the
+ // dialog to show a "try again" button for passive modalities.
+ mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
+ }
+
+ mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ private void handleOnError(int cookie, int error, String message) {
+ Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
+ // Errors can either be from the current auth session or the pending auth session.
+ // The pending auth session may receive errors such as ERROR_LOCKOUT before
+ // it becomes the current auth session. Similarly, the current auth session may
+ // receive errors such as ERROR_CANCELED while the pending auth session is preparing
+ // to be started. Thus we must match error messages with their cookies to be sure
+ // of their intended receivers.
+ try {
+ if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
+ if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
+ mStatusBarService.onBiometricError(message);
+ if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ mActivityTaskManager.unregisterTaskStackListener(
+ mTaskStackListener);
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ mStatusBarService.hideBiometricDialog();
+ } else {
+ // Send errors after the dialog is dismissed.
+ mHandler.postDelayed(() -> {
+ try {
+ if (mCurrentAuthSession != null) {
+ mActivityTaskManager.unregisterTaskStackListener(
+ mTaskStackListener);
+ mCurrentAuthSession.mClientReceiver.onError(error,
+ message);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }, BiometricPrompt.HIDE_DIALOG_DELAY);
+ }
+ } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
+ // In the "try again" state, we should forward canceled errors to
+ // the client and and clean up.
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mStatusBarService.onBiometricError(message);
+ mActivityTaskManager.unregisterTaskStackListener(
+ mTaskStackListener);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ } else {
+ Slog.e(TAG, "Impossible session error state: "
+ + mCurrentAuthSession.mState);
+ }
+ } else if (mPendingAuthSession != null
+ && mPendingAuthSession.containsCookie(cookie)) {
+ if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
+ mPendingAuthSession.mClientReceiver.onError(error, message);
+ mPendingAuthSession.mState = STATE_AUTH_IDLE;
+ mPendingAuthSession = null;
+ } else {
+ Slog.e(TAG, "Impossible pending session error state: "
+ + mPendingAuthSession.mState);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ private void handleOnAcquired(int acquiredInfo, String message) {
+ // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+ // after user dismissed/canceled dialog).
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "onAcquired(): Auth session is null");
+ return;
+ }
+
+ if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+ try {
+ mStatusBarService.onBiometricHelp(message);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ }
+
+ private void handleOnDismissed(int reason) {
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null");
+ return;
+ }
+
+ logDialogDismissed(reason);
+
+ try {
+ if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Positive button is used by passive modalities as a "confirm" button,
+ // do not send to client
+ mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
+ // Cancel authentication. Skip the token/package check since we are cancelling
+ // from system server. The interface is permission protected so this is fine.
+ cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+ }
+ if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_user_canceled));
+ } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Have the service send the token to KeyStore, and send onAuthenticated
+ // to the application
+ KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
+ mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ }
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ private void handleOnTryAgainPressed() {
+ Slog.d(TAG, "onTryAgainPressed");
+ // No need to check permission, since it can only be invoked by SystemUI
+ // (or system server itself).
+ authenticateInternal(mCurrentAuthSession.mToken,
+ mCurrentAuthSession.mSessionId,
+ mCurrentAuthSession.mUserId,
+ mCurrentAuthSession.mClientReceiver,
+ mCurrentAuthSession.mOpPackageName,
+ mCurrentAuthSession.mBundle,
+ mCurrentAuthSession.mCallingUid,
+ mCurrentAuthSession.mCallingPid,
+ mCurrentAuthSession.mCallingUserId,
+ mCurrentAuthSession.mModality);
+ }
+
+ private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation,
+ int userId) {
+ Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+ if (pair.getValue() == cookie) {
+ mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue());
+ mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey());
+ Slog.d(TAG, "Matched cookie: " + cookie + ", "
+ + mPendingAuthSession.mModalitiesWaiting.size() + " remaining");
+ break;
+ }
+ }
+
+ if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
+ final boolean continuing = mCurrentAuthSession != null
+ && mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
+
+ mCurrentAuthSession = mPendingAuthSession;
+ mPendingAuthSession = null;
+
+ mCurrentAuthSession.mState = STATE_AUTH_STARTED;
+ try {
+ int modality = TYPE_NONE;
+ it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+ if (pair.getKey() == TYPE_FINGERPRINT) {
+ mFingerprintService.startPreparedClient(pair.getValue());
+ } else if (pair.getKey() == TYPE_IRIS) {
+ Slog.e(TAG, "Iris unsupported");
+ } else if (pair.getKey() == TYPE_FACE) {
+ mFaceService.startPreparedClient(pair.getValue());
+ } else {
+ Slog.e(TAG, "Unknown modality: " + pair.getKey());
+ }
+ modality |= pair.getKey();
+ }
+
+ if (!continuing) {
+ mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
+ mInternalReceiver, modality, requireConfirmation, userId);
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ }
+
+ private void handleAuthenticate(IBinder token, long sessionId, int userId,
+ IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
+ int callingUid, int callingPid, int callingUserId) {
+
+ mHandler.post(() -> {
+ final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
+ final int modality = result.first;
+ final int error = result.second;
+
+ // Check for errors, notify callback, and return
+ if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
+ try {
+ final String hardwareUnavailable =
+ getContext().getString(R.string.biometric_error_hw_unavailable);
+ switch (error) {
+ case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
+ receiver.onError(error, hardwareUnavailable);
+ break;
+ case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
+ receiver.onError(error, hardwareUnavailable);
+ break;
+ case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
+ receiver.onError(error,
+ getErrorString(modality, error, 0 /* vendorCode */));
+ break;
+ default:
+ Slog.e(TAG, "Unhandled error");
+ break;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
+ }
+ return;
+ }
+
+ mCurrentModality = modality;
+
+ // Start preparing for authentication. Authentication starts when
+ // all modalities requested have invoked onReadyForAuthentication.
+ authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
+ callingUid, callingPid, callingUserId, modality);
+ });
+ }
+
+ /**
+ * authenticate() (above) which is called from BiometricPrompt determines which
+ * modality/modalities to start authenticating with. authenticateInternal() should only be
+ * used for:
+ * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
+ * invoked, shortly after which BiometricPrompt is shown and authentication starts
+ * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
+ * and the user has pressed "try again"
+ */
+ private void authenticateInternal(IBinder token, long sessionId, int userId,
+ IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
+ int callingUid, int callingPid, int callingUserId, int modality) {
+ try {
+ boolean requireConfirmation = bundle.getBoolean(
+ BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
+ if ((modality & TYPE_FACE) != 0) {
+ // Check if the user has forced confirmation to be required in Settings.
+ requireConfirmation = requireConfirmation
+ || mSettingObserver.getFaceAlwaysRequireConfirmation();
+ }
+ // Generate random cookies to pass to the services that should prepare to start
+ // authenticating. Store the cookie here and wait for all services to "ack"
+ // with the cookie. Once all cookies are received, we can show the prompt
+ // and let the services start authenticating. The cookie should be non-zero.
+ final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
+ Slog.d(TAG, "Creating auth session. Modality: " + modality
+ + ", cookie: " + cookie);
+ final HashMap<Integer, Integer> authenticators = new HashMap<>();
+ authenticators.put(modality, cookie);
+ mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
+ receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
+ modality, requireConfirmation);
+ mPendingAuthSession.mState = STATE_AUTH_CALLED;
+ // No polymorphism :(
+ if ((modality & TYPE_FINGERPRINT) != 0) {
+ mFingerprintService.prepareForAuthentication(token, sessionId, userId,
+ mInternalReceiver, opPackageName, cookie,
+ callingUid, callingPid, callingUserId);
+ }
+ if ((modality & TYPE_IRIS) != 0) {
+ Slog.w(TAG, "Iris unsupported");
+ }
+ if ((modality & TYPE_FACE) != 0) {
+ mFaceService.prepareForAuthentication(requireConfirmation,
+ token, sessionId, userId, mInternalReceiver, opPackageName,
+ cookie, callingUid, callingPid, callingUserId);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to start authentication", e);
+ }
+ }
+
+ private void handleCancelAuthentication(IBinder token, String opPackageName) {
+ if (token == null || opPackageName == null) {
+ Slog.e(TAG, "Unable to cancel, one or more null arguments");
+ return;
+ }
+
+ // We need to check the current authenticators state. If we're pending confirm
+ // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
+ // since we won't be getting an onError from the driver.
+ if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ try {
+ // Send error to client
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_user_canceled)
+ );
+
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ mStatusBarService.hideBiometricDialog();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ } else {
+ cancelInternal(token, opPackageName, true /* fromClient */);
+ }
+ }
+
+
+ void cancelInternal(IBinder token, String opPackageName, boolean fromClient) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+ mHandler.post(() -> {
+ try {
+ // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
+ // drivers have canceled authentication.
+ if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
+ mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ }
+ if ((mCurrentModality & TYPE_IRIS) != 0) {
+ Slog.w(TAG, "Iris unsupported");
+ }
+ if ((mCurrentModality & TYPE_FACE) != 0) {
+ mFaceService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to cancel authentication");
+ }
+ });
+ }
+
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index ebb9e06..3e48445 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -1223,6 +1223,9 @@
BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
} else {
clearEnumerateState();
+ if (mPendingClient != null) {
+ startClient(mPendingClient, false /* initiatedByClient */);
+ }
}
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index cefe583..14b7301 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -2494,24 +2494,33 @@
}
}
- // The expensive check last: validate that the defined package+service is
+ // The expensive check: validate that the defined package+service is
// still present & viable.
- final boolean componentPresent;
+ final ServiceInfo service;
try {
- componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
+ service = AppGlobals.getPackageManager().getServiceInfo(
job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- job.getUserId()) != null);
+ job.getUserId());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
- if (DEBUG) {
- Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
- + " componentPresent=" + componentPresent);
+ if (service == null) {
+ if (DEBUG) {
+ Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+ + " component not present");
+ }
+ return false;
}
// Everything else checked out so far, so this is the final yes/no check
- return componentPresent;
+ final boolean appIsBad = mActivityManagerInternal.isAppBad(service.applicationInfo);
+ if (DEBUG) {
+ if (appIsBad) {
+ Slog.i(TAG, "App is bad for " + job.toShortString() + " so not runnable");
+ }
+ }
+ return !appIsBad;
}
private void evaluateControllerStatesLocked(final JobStatus job) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a53ab84..293813a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2699,6 +2699,14 @@
}
}
+ @Override
+ public boolean hasPendingEscrowToken(int userId) {
+ checkPasswordReadPermission(userId);
+ synchronized (mSpManager) {
+ return !mSpManager.getPendingTokensForUser(userId).isEmpty();
+ }
+ }
+
private boolean removeEscrowToken(long handle, int userId) {
synchronized (mSpManager) {
if (handle == getSyntheticPasswordHandleLocked(userId)) {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 142ad53..1ba0e8c 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -751,7 +751,7 @@
/**
* Create a token based Synthetic password for the given user.
- * @return
+ * @return the handle of the token
*/
public long createTokenBasedSyntheticPassword(byte[] token, int userId,
@Nullable EscrowTokenStateChangeCallback changeCallback) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 7f057f0..18cfa4a 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -45,8 +45,6 @@
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
-import android.content.pm.Signature;
-import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.graphics.Rect;
import android.net.Uri;
@@ -62,31 +60,21 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
-import android.util.ByteStringUtils;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.StatLogger;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
/**
* Service that manages requests and callbacks for launchers that support
@@ -125,16 +113,6 @@
private static final boolean DEBUG = false;
private static final String TAG = "LauncherAppsService";
- // Stats
- @VisibleForTesting
- interface Stats {
- int INIT_VOUCHED_SIGNATURES = 0;
- int COUNT = INIT_VOUCHED_SIGNATURES + 1;
- }
- private final StatLogger mStatLogger = new StatLogger(new String[] {
- "initVouchedSignatures"
- });
-
private final Context mContext;
private final UserManager mUm;
private final UserManagerInternal mUserManagerInternal;
@@ -145,16 +123,11 @@
private final PackageCallbackList<IOnAppsChangedListener> mListeners
= new PackageCallbackList<IOnAppsChangedListener>();
private final DevicePolicyManager mDpm;
- private final ConcurrentHashMap<UserHandle, Set<String>> mVouchedSignaturesByUser;
- private final Set<String> mVouchProviders;
private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
- private final VouchesChangedMonitor mVouchesChangedMonitor = new VouchesChangedMonitor();
private final Handler mCallbackHandler;
- private final Object mVouchedSignaturesLocked = new Object();
-
private PackageInstallerService mPackageInstallerService;
public LauncherAppsImpl(Context context) {
@@ -173,9 +146,6 @@
mShortcutServiceInternal.addListener(mPackageMonitor);
mCallbackHandler = BackgroundThread.getHandler();
mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- mVouchedSignaturesByUser = new ConcurrentHashMap<>();
- mVouchProviders = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
- mVouchesChangedMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
}
@VisibleForTesting
@@ -468,32 +438,9 @@
if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
return false;
}
- if (!mVouchedSignaturesByUser.containsKey(user)) {
- initVouchedSignatures(user);
- }
if (isManagedProfileAdmin(user, appInfo.packageName)) {
return false;
}
- if (mVouchProviders.contains(appInfo.packageName)) {
- // If it's a vouching packages then we must show hidden app
- return true;
- }
- // If app's signature is in vouch list, do not show hidden app
- final Set<String> vouches = mVouchedSignaturesByUser.get(user);
- try {
- final PackageInfo pkgInfo = mContext.getPackageManager().getPackageInfo(
- appInfo.packageName, PackageManager.GET_SIGNING_CERTIFICATES);
- final Signature[] signatures = getLatestSignatures(pkgInfo.signingInfo);
- // If any of the signatures appears in vouches, then we don't show hidden app
- for (Signature signature : signatures) {
- final String certDigest = computePackageCertDigest(signature);
- if (vouches.contains(certDigest)) {
- return false;
- }
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Should not happen
- }
return true;
}
@@ -515,100 +462,6 @@
return false;
}
- @VisibleForTesting
- static String computePackageCertDigest(Signature signature) {
- MessageDigest messageDigest;
- try {
- messageDigest = MessageDigest.getInstance("SHA1");
- } catch (NoSuchAlgorithmException e) {
- // Should not happen
- return null;
- }
- messageDigest.update(signature.toByteArray());
- final byte[] digest = messageDigest.digest();
- return ByteStringUtils.toHexString(digest);
- }
-
- @VisibleForTesting
- static Signature[] getLatestSignatures(SigningInfo signingInfo) {
- if (signingInfo.hasMultipleSigners()) {
- return signingInfo.getApkContentsSigners();
- } else {
- final Signature[] signatures = signingInfo.getSigningCertificateHistory();
- return new Signature[]{signatures[0]};
- }
- }
-
- private void updateVouches(String packageName, UserHandle user) {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
- ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName,
- PackageManager.GET_META_DATA, Binder.getCallingUid(), user.getIdentifier());
- if (appInfo == null) {
- Log.w(TAG, "appInfo " + packageName + " is null");
- return;
- }
- updateVouches(appInfo, user);
- }
-
- private void updateVouches(ApplicationInfo appInfo, UserHandle user) {
- if (appInfo == null || appInfo.metaData == null) {
- // No meta-data
- return;
- }
- int tokenResourceId = appInfo.metaData.getInt(LauncherApps.VOUCHED_CERTS_KEY);
- if (tokenResourceId == 0) {
- // No xml file
- return;
- }
- mVouchProviders.add(appInfo.packageName);
- Set<String> vouches = mVouchedSignaturesByUser.get(user);
- try {
- List<String> signatures = Arrays.asList(
- mContext.getPackageManager().getResourcesForApplication(
- appInfo.packageName).getStringArray(tokenResourceId));
- for (String signature : signatures) {
- vouches.add(signature.toUpperCase());
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Should not happen
- }
- }
-
- private void initVouchedSignatures(UserHandle user) {
- synchronized (mVouchedSignaturesLocked) {
- if (mVouchedSignaturesByUser.contains(user)) {
- return;
- }
- final long startTime = mStatLogger.getTime();
-
- Set<String> vouches = Collections.newSetFromMap(
- new ConcurrentHashMap<String, Boolean>());
-
- final int callingUid = injectBinderCallingUid();
- long ident = Binder.clearCallingIdentity();
- try {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
- List<ApplicationInfo> installedPackages = pmInt.getInstalledApplications(
- PackageManager.GET_META_DATA, user.getIdentifier(), callingUid);
- for (ApplicationInfo appInfo : installedPackages) {
- updateVouches(appInfo, user);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- mVouchedSignaturesByUser.putIfAbsent(user, vouches);
- mStatLogger.logDurationStat(Stats.INIT_VOUCHED_SIGNATURES, startTime);
- }
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
- mStatLogger.dump(pw, " ");
- }
-
@Override
public ActivityInfo resolveActivity(
String callingPackage, ComponentName component, UserHandle user)
@@ -1022,18 +875,6 @@
mCallbackHandler.post(r);
}
- private class VouchesChangedMonitor extends PackageMonitor {
- @Override
- public void onPackageAdded(String packageName, int uid) {
- updateVouches(packageName, new UserHandle(getChangingUserId()));
- }
-
- @Override
- public void onPackageModified(String packageName) {
- updateVouches(packageName, new UserHandle(getChangingUserId()));
- }
- }
-
private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
// TODO Simplify with lambdas.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e5b6397..4e8ef71 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -160,9 +160,9 @@
import android.content.pm.InstantAppRequest;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
-import android.content.pm.PackageBackwardCompatibility;
import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
+import android.content.pm.PackageBackwardCompatibility;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
@@ -232,6 +232,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
+import android.os.storage.DiskInfo;
import android.os.storage.IStorageManager;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
@@ -245,6 +246,7 @@
import android.security.KeyStore;
import android.security.SystemKeyStore;
import android.service.pm.PackageServiceDumpProto;
+import android.stats.storage.StorageEnums;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
@@ -269,6 +271,7 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.StatsLog;
import android.util.TimingsTraceLog;
import android.util.Xml;
import android.util.jar.StrictJarFile;
@@ -1912,6 +1915,15 @@
// Send broadcast package appeared if external for all users
if (isExternal(res.pkg)) {
+ if (!update) {
+ int packageExternalStorageType =
+ getPackageExternalStorageType(res.pkg);
+ // If the package was installed externally, log it.
+ if (packageExternalStorageType != StorageEnums.UNKNOWN) {
+ StatsLog.write(StatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
+ packageExternalStorageType, res.pkg.packageName);
+ }
+ }
if (DEBUG_INSTALL) {
Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
}
@@ -2000,6 +2012,32 @@
}
}
+ /**
+ * Gets the type of the external storage a package is installed on.
+ * @param pkg The package for which to get the external storage type.
+ * @return {@link StorageEnum#TYPE_UNKNOWN} if it is not stored externally or the corresponding
+ * {@link StorageEnum} storage type value if it is.
+ */
+ private int getPackageExternalStorageType(PackageParser.Package pkg) {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ VolumeInfo volume = storage.findVolumeByUuid(pkg.applicationInfo.storageUuid.toString());
+ if (volume != null) {
+ DiskInfo disk = volume.getDisk();
+ if (disk != null) {
+ if (disk.isSd()) {
+ return StorageEnums.SD_CARD;
+ }
+ if (disk.isUsb()) {
+ return StorageEnums.USB;
+ }
+ if (isExternal(pkg)) {
+ return StorageEnums.OTHER;
+ }
+ }
+ }
+ return StorageEnums.UNKNOWN;
+ }
+
private StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index bcfbefe..b00cafd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3284,7 +3284,11 @@
}
private void updateImeParent() {
- final SurfaceControl newParent = computeImeParent();
+ // Force attaching IME to the display when magnifying, or it would be magnified with
+ // target app together.
+ final boolean shouldAttachToDisplay = (mMagnificationSpec != null);
+ final SurfaceControl newParent =
+ shouldAttachToDisplay ? mWindowingLayer : computeImeParent();
if (newParent != null) {
mPendingTransaction.reparent(mImeWindowsContainers.mSurfaceControl, newParent);
scheduleAnimation();
@@ -4699,6 +4703,8 @@
} else {
mMagnificationSpec = null;
}
+ // Re-parent IME's SurfaceControl when MagnificationSpec changed.
+ updateImeParent();
applyMagnificationSpec(getPendingTransaction(), spec);
getPendingTransaction().apply();
diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
index bdb76c2..bd4e542 100644
--- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
@@ -83,7 +83,12 @@
}
public void systemReady() {
- mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler);
+ // GestureDetector records statistics about gesture classification events to inform gesture
+ // usage trends. SystemGesturesPointerEventListener creates a lot of noise in these
+ // statistics because it passes every touch event though a GestureDetector. By creating an
+ // anonymous subclass of GestureDetector, these statistics will be recorded with a unique
+ // source name that can be filtered.
+ mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler) {};
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 77515258..6a07a45 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -338,12 +338,15 @@
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
+ assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
+ assertTrue(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
PRIMARY_USER_ID).getResponseCode();
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
+ assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java
deleted file mode 100644
index d7dc58d..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.server.pm;
-
-import static org.junit.Assert.assertEquals;
-
-import android.content.pm.PackageParser;
-import android.content.pm.Signature;
-import android.content.pm.SigningInfo;
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@Presubmit
-@RunWith(MockitoJUnitRunner.class)
-public class LauncherAppsServiceTest {
-
- private static final Signature SIGNATURE_1 = new Signature(new byte[]{0x00, 0x01, 0x02, 0x03});
- private static final Signature SIGNATURE_2 = new Signature(new byte[]{0x04, 0x05, 0x06, 0x07});
- private static final Signature SIGNATURE_3 = new Signature(new byte[]{0x08, 0x09, 0x10, 0x11});
-
- @Test
- public void testComputePackageCertDigest() {
- String digest = LauncherAppsService.LauncherAppsImpl.computePackageCertDigest(SIGNATURE_1);
- assertEquals("A02A05B025B928C039CF1AE7E8EE04E7C190C0DB", digest);
- }
-
- @Test
- public void testGetLatestSignaturesWithSingleCert() {
- SigningInfo signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
- new Signature[]{SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
- null,
- null));
- Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
- signingInfo);
- assertEquals(1, signatures.length);
- assertEquals(SIGNATURE_1, signatures[0]);
- }
-
- @Test
- public void testGetLatestSignaturesWithMultiCert() {
- SigningInfo signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
- new Signature[]{SIGNATURE_1, SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
- null,
- null));
- Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
- signingInfo);
- assertEquals(2, signatures.length);
- assertEquals(SIGNATURE_1, signatures[0]);
- assertEquals(SIGNATURE_2, signatures[1]);
- }
-
- @Test
- public void testGetLatestSignaturesWithCertHistory() {
- SigningInfo signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
- new Signature[]{SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
- null,
- new Signature[]{SIGNATURE_2, SIGNATURE_3}));
- Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
- signingInfo);
- assertEquals(1, signatures.length);
- assertEquals(SIGNATURE_2, signatures[0]);
- }
-
-}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 524d080..5f9ef3c 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1520,6 +1520,14 @@
"carrier_app_no_wake_signal_config";
/**
+ * Determines whether the carrier app needed to be involved when users try to finish setting up
+ * the SIM card to get network service.
+ * @hide
+ */
+ public static final String KEY_CARRIER_APP_REQUIRED_DURING_SIM_SETUP_BOOL =
+ "carrier_app_required_during_setup_bool";
+
+ /**
* Default value for {@link Settings.Global#DATA_ROAMING}
* @hide
*/
@@ -2307,9 +2315,9 @@
"undelivered_sms_message_expiration_time";
/**
- * Specifies a carrier-defined {@link CallRedirectionService} which Telecom will bind
- * to for outgoing calls. An empty string indicates that no carrier-defined
- * {@link CallRedirectionService} is specified.
+ * Specifies a carrier-defined {@link android.telecom.CallRedirectionService} which Telecom
+ * will bind to for outgoing calls. An empty string indicates that no carrier-defined
+ * {@link android.telecom.CallRedirectionService} is specified.
* @hide
*/
public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING =
@@ -2867,6 +2875,7 @@
sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
sDefaults.putString(KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_CALL_SCREENING_APP_STRING, "");
+ sDefaults.putString(KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING, null);
sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false);
sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, "");
sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
@@ -2922,6 +2931,7 @@
+ "com.android.internal.telephony.CARRIER_SIGNAL_RESET"
});
sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_CARRIER_APP_REQUIRED_DURING_SIM_SETUP_BOOL, false);
// Default carrier app configurations
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f7ab921..99032bc 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -58,6 +58,8 @@
import android.os.WorkSource;
import android.provider.Settings.SettingNotFoundException;
import android.service.carrier.CarrierIdentifier;
+import android.telecom.CallScreeningService;
+import android.telecom.InCallService;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -617,7 +619,13 @@
* <p class="note">
* Retrieve with
* {@link android.content.Intent#getStringExtra(String)}.
+ * <p>
+ *
+ * @deprecated Companion apps for wearable devices should use the {@link InCallService} API
+ * to retrieve the phone number for calls instead. Apps performing call screening should use
+ * the {@link CallScreeningService} API instead.
*/
+ @Deprecated
public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
/**
@@ -9857,8 +9865,11 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
+ *
+ * @deprecated
* @hide
*/
+ @Deprecated
@TestApi
public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
String gid2, String plmn, String spn) {
@@ -9866,7 +9877,35 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
telephony.setCarrierTestOverride(
- getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn);
+ getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn,
+ null, null);
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ }
+
+ /**
+ * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2,
+ * plmn, spn, apn and carrier priviledge. This would be handy for, eg, forcing a particular
+ * carrier id, carrier's config (also any country or carrier overlays) to be loaded when using
+ * a test SIM with a call box.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ *
+ * @hide
+ */
+ @TestApi
+ public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+ String gid2, String plmn, String spn,
+ String carrierPriviledgeRules, String apn) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setCarrierTestOverride(
+ getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn,
+ carrierPriviledgeRules, apn);
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index e651783..875bd78 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -285,6 +285,8 @@
* {@link #ACTION_DELETE_SUBSCRIPTION_PRIVILEGED}, and
* {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing the ID of the targeted subscription.
*
+ * <p>Expected type of the extra data: int
+ *
* @hide
*/
@SystemApi
@@ -295,6 +297,8 @@
* Key for an extra set on {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED} providing a boolean
* value of whether to enable or disable the targeted subscription.
*
+ * <p>Expected type of the extra data: boolean
+ *
* @hide
*/
@SystemApi
@@ -305,6 +309,8 @@
* Key for an extra set on {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing a new
* nickname for the targeted subscription.
*
+ * <p>Expected type of the extra data: String
+ *
* @hide
*/
@SystemApi
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 62b92fd..c2c31cc 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1683,7 +1683,7 @@
* (also any country or carrier overlays) to be loaded when using a test SIM with a call box.
*/
void setCarrierTestOverride(int subId, String mccmnc, String imsi, String iccid, String gid1,
- String gid2, String plmn, String spn);
+ String gid2, String plmn, String spn, String carrierPrivilegeRules, String apn);
/**
* A test API to return installed carrier id list version.
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 7a505a2..950c6f8 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -139,6 +139,8 @@
private X509Certificate[] mClientCertificateChain;
private int mEapMethod = Eap.NONE;
private int mPhase2Method = Phase2.NONE;
+ private boolean mIsAppInstalledDeviceKeyAndCert = false;
+ private boolean mIsAppInstalledCaCert = false;
private static final String TAG = "WifiEnterpriseConfig";
@@ -181,6 +183,8 @@
}
mEapMethod = source.mEapMethod;
mPhase2Method = source.mPhase2Method;
+ mIsAppInstalledDeviceKeyAndCert = source.mIsAppInstalledDeviceKeyAndCert;
+ mIsAppInstalledCaCert = source.mIsAppInstalledCaCert;
}
/**
@@ -224,6 +228,8 @@
ParcelUtil.writeCertificates(dest, mCaCerts);
ParcelUtil.writePrivateKey(dest, mClientPrivateKey);
ParcelUtil.writeCertificates(dest, mClientCertificateChain);
+ dest.writeBoolean(mIsAppInstalledDeviceKeyAndCert);
+ dest.writeBoolean(mIsAppInstalledCaCert);
}
public static final @android.annotation.NonNull Creator<WifiEnterpriseConfig> CREATOR =
@@ -243,6 +249,8 @@
enterpriseConfig.mCaCerts = ParcelUtil.readCertificates(in);
enterpriseConfig.mClientPrivateKey = ParcelUtil.readPrivateKey(in);
enterpriseConfig.mClientCertificateChain = ParcelUtil.readCertificates(in);
+ enterpriseConfig.mIsAppInstalledDeviceKeyAndCert = in.readBoolean();
+ enterpriseConfig.mIsAppInstalledCaCert = in.readBoolean();
return enterpriseConfig;
}
@@ -652,8 +660,10 @@
public void setCaCertificate(@Nullable X509Certificate cert) {
if (cert != null) {
if (cert.getBasicConstraints() >= 0) {
+ mIsAppInstalledCaCert = true;
mCaCerts = new X509Certificate[] {cert};
} else {
+ mCaCerts = null;
throw new IllegalArgumentException("Not a CA certificate");
}
} else {
@@ -694,10 +704,12 @@
if (certs[i].getBasicConstraints() >= 0) {
newCerts[i] = certs[i];
} else {
+ mCaCerts = null;
throw new IllegalArgumentException("Not a CA certificate");
}
}
mCaCerts = newCerts;
+ mIsAppInstalledCaCert = true;
} else {
mCaCerts = null;
}
@@ -853,6 +865,7 @@
mClientPrivateKey = privateKey;
mClientCertificateChain = newCerts;
+ mIsAppInstalledDeviceKeyAndCert = true;
}
/**
@@ -1147,4 +1160,30 @@
}
return true;
}
+
+ /**
+ * Check if certificate was installed by an app, or manually (not by an app). If true,
+ * certificate and keys will be removed from key storage when this network is removed. If not,
+ * then certificates and keys remain persistent until the user manually removes them.
+ *
+ * @return true if certificate was installed by an app, false if certificate was installed
+ * manually by the user.
+ * @hide
+ */
+ public boolean isAppInstalledDeviceKeyAndCert() {
+ return mIsAppInstalledDeviceKeyAndCert;
+ }
+
+ /**
+ * Check if CA certificate was installed by an app, or manually (not by an app). If true,
+ * CA certificate will be removed from key storage when this network is removed. If not,
+ * then certificates and keys remain persistent until the user manually removes them.
+ *
+ * @return true if CA certificate was installed by an app, false if CA certificate was installed
+ * manually by the user.
+ * @hide
+ */
+ public boolean isAppInstalledCaCert() {
+ return mIsAppInstalledCaCert;
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 32a7a47..a9c9939 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -403,7 +403,7 @@
* .setWpa3Passphrase("test6789")
* .build()
* final List<WifiNetworkSuggestion> suggestionsList =
- * new ArrayList<WifiNetworkSuggestion> {{
+ * new ArrayList<WifiNetworkSuggestion> {{
* add(suggestion1);
* add(suggestion2);
* add(suggestion3);
diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index 6ec64ff..beed666 100644
--- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -423,4 +423,68 @@
mEnterpriseConfig.setPassword(password);
assertFalse(mEnterpriseConfig.toString().contains(password));
}
+
+ /** Verifies that certificate ownership flag is set correctly */
+ @Test
+ public void testIsAppInstalledDeviceKeyAndCert() {
+ // First make sure that app didn't install anything
+ assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert());
+ assertFalse(mEnterpriseConfig.isAppInstalledCaCert());
+
+ // Then app loads keys via the enterprise config API
+ PrivateKey clientKey = FakeKeys.RSA_KEY1;
+ X509Certificate cert0 = FakeKeys.CLIENT_CERT;
+ X509Certificate cert1 = FakeKeys.CA_CERT1;
+ X509Certificate[] clientChain = new X509Certificate[] {cert0, cert1};
+ mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain);
+ X509Certificate[] result = mEnterpriseConfig.getClientCertificateChain();
+ assertEquals(result.length, 2);
+ assertTrue(result[0] == cert0 && result[1] == cert1);
+ assertTrue(mEnterpriseConfig.getClientCertificate() == cert0);
+
+ // Make sure it is the owner now
+ assertTrue(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert());
+ assertFalse(mEnterpriseConfig.isAppInstalledCaCert());
+ }
+
+ /** Verifies that certificate ownership flag is set correctly */
+ @Test
+ public void testIsAppInstalledCaCert() {
+ // First make sure that app didn't install anything
+ assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert());
+ assertFalse(mEnterpriseConfig.isAppInstalledCaCert());
+
+ // Then app loads CA cert via the enterprise config API
+ X509Certificate cert = FakeKeys.CA_CERT1;
+ mEnterpriseConfig.setCaCertificate(cert);
+ X509Certificate result = mEnterpriseConfig.getCaCertificate();
+ assertTrue(result == cert);
+
+ // Make sure it is the owner now
+ assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert());
+ assertTrue(mEnterpriseConfig.isAppInstalledCaCert());
+ }
+
+ /** Verifies that certificate ownership flag is set correctly */
+ @Test
+ public void testIsAppInstalledCaCerts() {
+ // First make sure that app didn't install anything
+ assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert());
+ assertFalse(mEnterpriseConfig.isAppInstalledCaCert());
+
+ // Then app loads CA cert via the enterprise config API
+ X509Certificate cert0 = FakeKeys.CA_CERT0;
+ X509Certificate cert1 = FakeKeys.CA_CERT1;
+ X509Certificate[] cert = new X509Certificate[] {cert0, cert1};
+
+ mEnterpriseConfig.setCaCertificates(cert);
+ X509Certificate[] result = mEnterpriseConfig.getCaCertificates();
+ assertEquals(result.length, 2);
+ assertTrue(result[0] == cert0 && result[1] == cert1);
+// assertTrue(mEnterpriseConfig.getClientCertificate() == cert0);
+
+ // Make sure it is the owner now
+ assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert());
+ assertTrue(mEnterpriseConfig.isAppInstalledCaCert());
+ }
}