Merge "Fixes swipe gesture on non-dismissable notifications."
diff --git a/api/current.txt b/api/current.txt
index cde7499..84f5fcf 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -5370,6 +5370,7 @@
field public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR;
field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
+ field public static final int SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION = 11; // 0xb
field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
@@ -24464,6 +24465,7 @@
}
public static final class MediaExtractor.CasInfo {
+ method public byte[] getPrivateData();
method public android.media.MediaCas.Session getSession();
method public int getSystemId();
}
@@ -29000,11 +29002,14 @@
method public boolean is5GHzBandSupported();
method public boolean isDeviceToApRttSupported();
method public boolean isEnhancedPowerReportingSupported();
+ method public boolean isOweSupported();
method public boolean isP2pSupported();
method public boolean isPreferredNetworkOffloadSupported();
method public deprecated boolean isScanAlwaysAvailable();
method public boolean isTdlsSupported();
method public boolean isWifiEnabled();
+ method public boolean isWpa3SaeSupported();
+ method public boolean isWpa3SuiteBSupported();
method public deprecated boolean pingSupplicant();
method public deprecated boolean reassociate();
method public deprecated boolean reconnect();
diff --git a/api/system-current.txt b/api/system-current.txt
index 2c2df81..da46015 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -137,6 +137,7 @@
field public static final java.lang.String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
field public static final java.lang.String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
+ field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final java.lang.String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final java.lang.String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
field public static final java.lang.String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
@@ -3639,6 +3640,7 @@
public class WifiManager {
method public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
+ method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method public int getWifiApState();
@@ -5461,6 +5463,7 @@
method public static android.os.PersistableBundle getDefaultConfig();
method public void overrideConfig(int, android.os.PersistableBundle);
method public void updateConfigForPhoneId(int, java.lang.String);
+ field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
}
@@ -5535,8 +5538,10 @@
public class PhoneStateListener {
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
+ method public void onVoiceActivationStateChanged(int);
field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
field public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
+ field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
}
public class ServiceState implements android.os.Parcelable {
@@ -6600,6 +6605,22 @@
method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
}
+ public class ProvisioningManager {
+ method public static android.telephony.ims.ProvisioningManager createForSubscriptionId(android.content.Context, int);
+ method public int getProvisioningIntValue(int);
+ method public java.lang.String getProvisioningStringValue(int);
+ method public void registerProvisioningChangedCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.Callback);
+ method public int setProvisioningIntValue(int, int);
+ method public int setProvisioningStringValue(int, java.lang.String);
+ method public void unregisterProvisioningChangedCallback(android.telephony.ims.ProvisioningManager.Callback);
+ }
+
+ public static class ProvisioningManager.Callback {
+ ctor public ProvisioningManager.Callback();
+ method public void onProvisioningIntChanged(int, int);
+ method public void onProvisioningStringChanged(int, java.lang.String);
+ }
+
}
package android.telephony.ims.feature {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 53d9673..e6efc4c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -148,6 +148,11 @@
UserRestrictionChanged user_restriction_changed = 96;
SettingsUIChanged settings_ui_changed = 97;
ConnectivityStateChanged connectivity_state_changed = 98;
+ // TODO: service state change is very noisy shortly after boot, as well
+ // as at other transitions - coming out of doze, device plugged in, etc.
+ // Consider removing this if it becomes a problem
+ ServiceStateChanged service_state_changed = 99;
+ ServiceLaunchReported service_launch_reported = 100;
}
// Pulled events will start at field 10000.
@@ -192,6 +197,9 @@
NativeProcessMemoryState native_process_memory_state = 10036;
CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
OnDevicePowerMeasurement on_device_power_measurement = 10038;
+ DeviceCalculatedPowerUse device_calculated_power_use = 10039;
+ DeviceCalculatedPowerBlameUid device_calculated_power_blame_uid = 10040;
+ DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -2135,16 +2143,51 @@
* Logged in StatsCompanionService.java
*/
message ConnectivityStateChanged {
- // Id of the network.
- optional int32 net_id = 1;
+ // Id of the network.
+ optional int32 net_id = 1;
+
+ enum State {
+ UNKNOWN = 0;
+ CONNECTED = 1;
+ DISCONNECTED = 2;
+ }
+ // Connected state of a network.
+ optional State state = 2;
+}
+
+/**
+ * Logs when a service starts and stops.
+ * Logged from:
+ * services/core/java/com/android/server/am/ActiveServices.java
+ */
+message ServiceStateChanged {
+
+ optional int32 uid = 1 [(is_uid) = true];
+
+ optional string package_name = 2;
+
+ optional string service_name = 3;
enum State {
- UNKNOWN = 0;
- CONNECTED = 1;
- DISCONNECTED = 2;
+ START = 1;
+ STOP = 2;
}
- // Connected state of a network.
- optional State state = 2;
+
+ optional State state = 4;
+}
+
+/**
+ * Logs when a service is launched.
+ * Logged from:
+ * services/core/java/com/android/server/am/ActiveServices.java
+ */
+message ServiceLaunchReported {
+
+ optional int32 uid = 1 [(is_uid) = true];
+
+ optional string package_name = 2;
+
+ optional string service_name = 3;
}
//////////////////////////////////////////////////////////////////////
@@ -3198,3 +3241,63 @@
// Time spent in frequency in milliseconds, since thread start.
optional uint32 time_millis = 7;
}
+
+/**
+ * Pulls on-device BatteryStats power use calculations for the overall device.
+ */
+message DeviceCalculatedPowerUse {
+ // Power used by the device in mAh, as computed by BatteryStats, since BatteryStats last reset
+ // (i.e. roughly since device was last significantly charged).
+ // Currently, this is BatteryStatsHelper.getComputedPower() (not getTotalPower()).
+ optional float computed_power_milli_amp_hours = 1;
+}
+
+/**
+ * Pulls on-device BatteryStats power use calculations broken down by uid.
+ * This atom should be complemented by DeviceCalculatedPowerBlameOther, which contains the power use
+ * that is attributed to non-uid items. They must all be included to get the total power use.
+ */
+message DeviceCalculatedPowerBlameUid {
+ // Uid being blamed. Note: isolated uids have already been mapped to host uid.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Power used by this uid in mAh, as computed by BatteryStats, since BatteryStats last reset
+ // (i.e. roughly since device was last significantly charged).
+ optional float power_milli_amp_hours = 2;
+}
+
+/**
+ * Pulls on-device BatteryStats power use calculations that are not due to a uid, broken down by
+ * drain type.
+ * This atom should be complemented by DeviceCalculatedPowerBlameUid, which contains the blame that
+ * is attributed uids. They must all be included to get the total power use.
+ */
+message DeviceCalculatedPowerBlameOther {
+ // The type of item whose power use is being reported.
+ enum DrainType {
+ AMBIENT_DISPLAY = 0;
+ // reserved 1; reserved "APP"; // Logged instead in DeviceCalculatedPowerBlameUid.
+ BLUETOOTH = 2;
+ CAMERA = 3;
+ // Cell-standby
+ CELL = 4;
+ FLASHLIGHT = 5;
+ IDLE = 6;
+ MEMORY = 7;
+ // Amount that total computed drain exceeded the drain estimated using the
+ // battery level changes and capacity.
+ OVERCOUNTED = 8;
+ PHONE = 9;
+ SCREEN = 10;
+ // Amount that total computed drain was below the drain estimated using the
+ // battery level changes and capacity.
+ UNACCOUNTED = 11;
+ // reserved 12; reserved "USER"; // Entire drain for a user. This is NOT supported.
+ WIFI = 13;
+ }
+ optional DrainType drain_type = 1;
+
+ // Power used by this item in mAh, as computed by BatteryStats, since BatteryStats last reset
+ // (i.e. roughly since device was last significantly charged).
+ optional float power_milli_amp_hours = 2;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 8378ae1..0e131cb 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -243,6 +243,20 @@
{2, 3, 4, 5, 6},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
+ // DeviceCalculatedPowerUse.
+ {android::util::DEVICE_CALCULATED_POWER_USE,
+ {{}, {}, 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
+ // DeviceCalculatedPowerBlameUid.
+ {android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
+ {{}, {}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
+ // DeviceCalculatedPowerBlameOther.
+ {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
+ {{}, {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index bd9cf6d..362f4ae 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -88,6 +88,8 @@
ParceledListSlice getRecentNotifyingAppsForUser(int userId);
int getBlockedAppCount(int userId);
boolean areChannelsBypassingDnd();
+ int getAppsBypassingDndCount(int uid);
+ ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
// TODO: Remove this when callers have been migrated to the equivalent
// INotificationListener method.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index df37a02..450efdf 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -207,7 +207,8 @@
private static final int MAX_REPLY_HISTORY = 5;
/**
- * Maximum numbers of action buttons in a notification.
+ * Maximum number of (generic) action buttons in a notification (contextual action buttons are
+ * handled separately).
* @hide
*/
public static final int MAX_ACTION_BUTTONS = 3;
@@ -1421,6 +1422,12 @@
*/
public static final int SEMANTIC_ACTION_CALL = 10;
+ /**
+ * {@code SemanticAction}: Contextual action - dependent on the current notification. E.g.
+ * open a Map application with an address shown in the notification.
+ */
+ public static final int SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION = 11;
+
private final Bundle mExtras;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Icon mIcon;
@@ -2042,7 +2049,8 @@
SEMANTIC_ACTION_UNMUTE,
SEMANTIC_ACTION_THUMBS_UP,
SEMANTIC_ACTION_THUMBS_DOWN,
- SEMANTIC_ACTION_CALL
+ SEMANTIC_ACTION_CALL,
+ SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION
})
@Retention(RetentionPolicy.SOURCE)
public @interface SemanticAction {}
@@ -4962,6 +4970,18 @@
result);
}
+ private static List<Notification.Action> filterOutContextualActions(
+ List<Notification.Action> actions) {
+ List<Notification.Action> nonContextualActions = new ArrayList<>();
+ for (Notification.Action action : actions) {
+ if (action.getSemanticAction()
+ != Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+ nonContextualActions.add(action);
+ }
+ }
+ return nonContextualActions;
+ }
+
private RemoteViews applyStandardTemplateWithActions(int layoutId,
StandardTemplateParams p, TemplateBindResult result) {
RemoteViews big = applyStandardTemplate(layoutId, p, result);
@@ -4970,7 +4990,11 @@
boolean validRemoteInput = false;
- int N = mActions.size();
+ // In the UI contextual actions appear separately from the standard actions, so we
+ // filter them out here.
+ List<Notification.Action> nonContextualActions = filterOutContextualActions(mActions);
+
+ int N = nonContextualActions.size();
boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
if (N > 0) {
@@ -4979,7 +5003,8 @@
big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
for (int i=0; i<N; i++) {
- Action action = mActions.get(i);
+ Action action = nonContextualActions.get(i);
+
boolean actionHasValidInput = hasValidRemoteInput(action);
validRemoteInput |= actionHasValidInput;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 03eba7e..004417b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4335,6 +4335,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {android.os.IIdmap2} for managing idmap files (used by overlay
+ * packages).
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String IDMAP_SERVICE = "idmap";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link VrManager} for accessing the VR service.
*
* @see #getSystemService(String)
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 5f23749..4371c77 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -58,6 +58,7 @@
public final class AssetManager implements AutoCloseable {
private static final String TAG = "AssetManager";
private static final boolean DEBUG_REFS = false;
+ private static final boolean FEATURE_FLAG_IDMAP2 = false;
private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
@@ -195,13 +196,23 @@
return;
}
- // Make sure that all IDMAPs are up to date.
- nativeVerifySystemIdmaps();
try {
final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
- loadStaticRuntimeOverlays(apkAssets);
+ if (FEATURE_FLAG_IDMAP2) {
+ final String[] systemIdmapPaths =
+ nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
+ if (systemIdmapPaths == null) {
+ throw new IOException("idmap2 scan failed");
+ }
+ for (String idmapPath : systemIdmapPaths) {
+ apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
+ }
+ } else {
+ nativeVerifySystemIdmaps();
+ loadStaticRuntimeOverlays(apkAssets);
+ }
sSystemApkAssetsSet = new ArraySet<>(apkAssets);
sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
@@ -1404,6 +1415,7 @@
private static native long nativeAssetGetRemainingLength(long assetPtr);
private static native void nativeVerifySystemIdmaps();
+ private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
// Global debug native methods.
/**
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 72e1ab9..866bd9a 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -104,14 +104,6 @@
}
/**
- * Write a double value.
- */
- public void writeDouble(double val) {
- mTypes.add(EVENT_TYPE_DOUBLE);
- mValues.add(val);
- }
-
- /**
* Write a storage value.
*/
public void writeStorage(byte[] val) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e3401e7..b266648 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10324,6 +10324,18 @@
private static final Validator WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR =
BOOLEAN_VALIDATOR;
+ /**
+ * Setting to enable including recency information when determining pno network priorities.
+ * Disabled by default, and setting it to 1 will enable it.
+ * The value is boolean (0 or 1).
+ * @hide
+ */
+ public static final String WIFI_PNO_RECENCY_SORTING_ENABLED =
+ "wifi_pno_recency_sorting_enabled";
+
+ private static final Validator WIFI_PNO_RECENCY_SORTING_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* The maximum number of times we will retry a connection to an access
* point for which we have failed in acquiring an IP address from DHCP.
@@ -12799,6 +12811,8 @@
VALIDATORS.put(DEVICE_DEMO_MODE, BOOLEAN_VALIDATOR);
VALIDATORS.put(WIFI_PNO_FREQUENCY_CULLING_ENABLED,
WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR);
+ VALIDATORS.put(WIFI_PNO_RECENCY_SORTING_ENABLED,
+ WIFI_PNO_RECENCY_SORTING_ENABLED_VALIDATOR);
}
/**
diff --git a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
index 0000b9f..ac5be06 100644
--- a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
+++ b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
@@ -30,4 +30,6 @@
in IRoleManagerCallback callback);
void onClearRoleHolders(in String roleName, in IRoleManagerCallback callback);
+
+ void onGrantDefaultRoles(in IRoleManagerCallback callback);
}
diff --git a/core/java/android/rolecontrollerservice/RoleControllerService.java b/core/java/android/rolecontrollerservice/RoleControllerService.java
index da11bca..44c45bb 100644
--- a/core/java/android/rolecontrollerservice/RoleControllerService.java
+++ b/core/java/android/rolecontrollerservice/RoleControllerService.java
@@ -89,6 +89,13 @@
RoleControllerService.this.onClearRoleHolders(roleName,
new RoleManagerCallbackDelegate(callback));
}
+
+ @Override
+ public void onGrantDefaultRoles(IRoleManagerCallback callback) {
+ Preconditions.checkNotNull(callback, "callback cannot be null");
+ RoleControllerService.this.onGrantDefaultRoles(
+ new RoleManagerCallbackDelegate(callback));
+ }
};
}
@@ -133,6 +140,16 @@
public abstract void onClearRoleHolders(@NonNull String roleName,
@NonNull RoleManagerCallback callback);
+ /**
+ * Called by system to grant default permissions and roles.
+ * <p>
+ * This is typically when creating a new user or upgrading either system or
+ * permission controller package
+ *
+ * @param callback the callback for whether this call is successful
+ */
+ public abstract void onGrantDefaultRoles(@NonNull RoleManagerCallback callback);
+
private static class RoleManagerCallbackDelegate implements RoleManagerCallback {
private IRoleManagerCallback mCallback;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index c8e0dd2..2d5f3bf 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1270,7 +1270,13 @@
*/
public float getLineLeft(int line) {
final int dir = getParagraphDirection(line);
- final Alignment align = getParagraphAlignment(line);
+ Alignment align = getParagraphAlignment(line);
+ // Before Q, StaticLayout.Builder.setAlignment didn't check whether the input alignment
+ // is null. And when it is null, the old behavior is the same as ALIGN_CENTER.
+ // To keep consistency, we convert a null alignment to ALIGN_CENTER.
+ if (align == null) {
+ align = Alignment.ALIGN_CENTER;
+ }
// First convert combinations of alignment and direction settings to
// three basic cases: ALIGN_LEFT, ALIGN_RIGHT and ALIGN_CENTER.
@@ -1319,7 +1325,13 @@
*/
public float getLineRight(int line) {
final int dir = getParagraphDirection(line);
- final Alignment align = getParagraphAlignment(line);
+ Alignment align = getParagraphAlignment(line);
+ // Before Q, StaticLayout.Builder.setAlignment didn't check whether the input alignment
+ // is null. And when it is null, the old behavior is the same as ALIGN_CENTER.
+ // To keep consistency, we convert a null alignment to ALIGN_CENTER.
+ if (align == null) {
+ align = Alignment.ALIGN_CENTER;
+ }
final Alignment resultAlign;
switch(align) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 7b638b4..6eb433a 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -201,7 +201,7 @@
}
}
- mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ mCharsValid = hasReplacement;
if (mCharsValid) {
if (mChars == null || mChars.length < mLen) {
@@ -815,7 +815,6 @@
} else {
final int delta = mStart;
if (mComputed == null) {
- // TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text.
return wp.getRunAdvance(mText, delta + start, delta + end,
delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
} else {
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 043b31d..03d9955 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -20,6 +20,7 @@
import android.graphics.BaseRecordingCanvas;
import android.graphics.CanvasProperty;
import android.graphics.Paint;
+import android.os.Build;
/**
* This class exists temporarily to workaround broken apps
@@ -36,13 +37,13 @@
}
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public abstract void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top,
CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx,
CanvasProperty<Float> ry, CanvasProperty<Paint> paint);
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
}
diff --git a/services/core/java/com/android/server/input/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
similarity index 97%
rename from services/core/java/com/android/server/input/InputApplicationHandle.java
rename to core/java/android/view/InputApplicationHandle.java
index 3cf7edc..dc1e505 100644
--- a/services/core/java/com/android/server/input/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.input;
+package android.view;
/**
* Functions as a handle for an application that can receive input.
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index b2dd6ac..84c8e7a 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -18,6 +18,7 @@
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
+import android.os.IBinder;
import android.os.Parcelable;
import android.util.Slog;
@@ -50,15 +51,17 @@
@SuppressWarnings("unused")
@UnsupportedAppUsage
private long mPtr; // used by native code
-
+
private static native InputChannel[] nativeOpenInputChannelPair(String name);
-
+
private native void nativeDispose(boolean finalized);
private native void nativeTransferTo(InputChannel other);
private native void nativeReadFromParcel(Parcel parcel);
private native void nativeWriteToParcel(Parcel parcel);
private native void nativeDup(InputChannel target);
-
+ private native IBinder nativeGetToken();
+ private native void nativeSetToken(IBinder token);
+
private native String nativeGetName();
/**
@@ -159,14 +162,22 @@
}
nativeWriteToParcel(out);
-
+
if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0) {
dispose();
}
}
-
+
@Override
public String toString() {
return getName();
}
+
+ public IBinder getToken() {
+ return nativeGetToken();
+ }
+
+ public void setToken(IBinder token) {
+ nativeSetToken(token);
+ }
}
diff --git a/services/core/java/com/android/server/input/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
similarity index 93%
rename from services/core/java/com/android/server/input/InputWindowHandle.java
rename to core/java/android/view/InputWindowHandle.java
index bb29bf8..621ee89 100644
--- a/services/core/java/com/android/server/input/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.input;
+package android.view;
import android.graphics.Region;
import android.view.IWindow;
@@ -34,9 +34,6 @@
// The input application handle.
public final InputApplicationHandle inputApplicationHandle;
- // The window manager's window state.
- public final Object windowState;
-
// The client window.
public final IWindow clientWindow;
@@ -97,9 +94,8 @@
private native void nativeDispose();
public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
- Object windowState, IWindow clientWindow, int displayId) {
+ IWindow clientWindow, int displayId) {
this.inputApplicationHandle = inputApplicationHandle;
- this.windowState = windowState;
this.clientWindow = clientWindow;
this.displayId = displayId;
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3d16eb8..a7a5024 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -153,6 +153,9 @@
private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken);
+ private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
+ InputWindowHandle handle);
+
private final CloseGuard mCloseGuard = CloseGuard.get();
private final String mName;
@@ -1459,6 +1462,12 @@
return this;
}
+ public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {
+ sc.checkNotReleased();
+ nativeSetInputWindowInfo(mNativeObject, sc.mNativeObject, handle);
+ return this;
+ }
+
@UnsupportedAppUsage
public Transaction setMatrix(SurfaceControl sc,
float dsdx, float dtdx, float dtdy, float dsdy) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c7f25d7..5f1336f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4732,6 +4732,16 @@
private TouchDelegate mTouchDelegate = null;
/**
+ * While touch exploration is in use, set to true when hovering across boundaries and
+ * inside the touch area of the delegate at receiving {@link MotionEvent#ACTION_HOVER_ENTER}
+ * or {@link MotionEvent#ACTION_HOVER_MOVE}. False when leaving boundaries or receiving a
+ * {@link MotionEvent#ACTION_HOVER_EXIT}.
+ * Note that children of view group are excluded in the touch area.
+ * @see #dispatchTouchExplorationHoverEvent
+ */
+ private boolean mHoveringTouchDelegate = false;
+
+ /**
* Solid color to use as a background when creating the drawing cache. Enables
* the cache to use 16 bit bitmaps instead of 32 bit.
*/
@@ -13965,6 +13975,96 @@
}
/**
+ * Dispatching hover events to {@link TouchDelegate} to improve accessibility.
+ * <p>
+ * This method is dispatching hover events to the delegate target to support explore by touch.
+ * Similar to {@link ViewGroup#dispatchTouchEvent}, this method send proper hover events to
+ * the delegate target according to the pointer and the touch area of the delegate while touch
+ * exploration enabled.
+ * </p>
+ *
+ * @param event The motion event dispatch to the delegate target.
+ * @return True if the event was handled, false otherwise.
+ *
+ * @see #onHoverEvent
+ */
+ private boolean dispatchTouchExplorationHoverEvent(MotionEvent event) {
+ final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
+ if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) {
+ return false;
+ }
+
+ final boolean oldHoveringTouchDelegate = mHoveringTouchDelegate;
+ final int action = event.getActionMasked();
+ boolean pointInDelegateRegion = false;
+ boolean handled = false;
+
+ final AccessibilityNodeInfo.TouchDelegateInfo info = mTouchDelegate.getTouchDelegateInfo();
+ for (int i = 0; i < info.getRegionCount(); i++) {
+ Region r = info.getRegionAt(i);
+ if (r.contains((int) event.getX(), (int) event.getY())) {
+ pointInDelegateRegion = true;
+ }
+ }
+
+ // Explore by touch should dispatch events to children under the pointer first if any
+ // before dispatching to TouchDelegate. For non-hoverable views that do not consume
+ // hover events but receive accessibility focus, it should also not delegate to these
+ // views when hovered.
+ if (!oldHoveringTouchDelegate) {
+ if ((action == MotionEvent.ACTION_HOVER_ENTER
+ || action == MotionEvent.ACTION_HOVER_MOVE)
+ && !pointInHoveredChild(event)
+ && pointInDelegateRegion) {
+ mHoveringTouchDelegate = true;
+ }
+ } else {
+ if (action == MotionEvent.ACTION_HOVER_EXIT
+ || (action == MotionEvent.ACTION_HOVER_MOVE
+ && (pointInHoveredChild(event) || !pointInDelegateRegion))) {
+ mHoveringTouchDelegate = false;
+ }
+ }
+ switch (action) {
+ case MotionEvent.ACTION_HOVER_MOVE:
+ if (oldHoveringTouchDelegate && mHoveringTouchDelegate) {
+ // Inside bounds, dispatch as is.
+ handled = mTouchDelegate.onTouchExplorationHoverEvent(event);
+ } else if (!oldHoveringTouchDelegate && mHoveringTouchDelegate) {
+ // Moving inbound, synthesize hover enter.
+ MotionEvent eventNoHistory = (event.getHistorySize() == 0)
+ ? event : MotionEvent.obtainNoHistory(event);
+ eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
+ handled = mTouchDelegate.onTouchExplorationHoverEvent(eventNoHistory);
+ eventNoHistory.setAction(action);
+ handled |= mTouchDelegate.onTouchExplorationHoverEvent(eventNoHistory);
+ } else if (oldHoveringTouchDelegate && !mHoveringTouchDelegate) {
+ // Moving outbound, synthesize hover exit.
+ final boolean hoverExitPending = event.isHoverExitPending();
+ event.setHoverExitPending(true);
+ mTouchDelegate.onTouchExplorationHoverEvent(event);
+ MotionEvent eventNoHistory = (event.getHistorySize() == 0)
+ ? event : MotionEvent.obtainNoHistory(event);
+ eventNoHistory.setHoverExitPending(hoverExitPending);
+ eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
+ mTouchDelegate.onTouchExplorationHoverEvent(eventNoHistory);
+ } // else: outside bounds, do nothing.
+ break;
+ case MotionEvent.ACTION_HOVER_ENTER:
+ if (!oldHoveringTouchDelegate && mHoveringTouchDelegate) {
+ handled = mTouchDelegate.onTouchExplorationHoverEvent(event);
+ }
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ if (oldHoveringTouchDelegate) {
+ mTouchDelegate.onTouchExplorationHoverEvent(event);
+ }
+ break;
+ }
+ return handled;
+ }
+
+ /**
* Implement this method to handle hover events.
* <p>
* This method is called whenever a pointer is hovering into, over, or out of the
@@ -14001,15 +14101,8 @@
* @see #onHoverChanged
*/
public boolean onHoverEvent(MotionEvent event) {
- // Explore by touch should dispatch events to children under pointer first if any before
- // dispatching to TouchDelegate. For children non-hoverable that will not consume events,
- // it should also not delegate when they got the pointer hovered.
- if (mTouchDelegate != null && !pointInHoveredChild(event)) {
- final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
- if (manager.isEnabled() && manager.isTouchExplorationEnabled()
- && mTouchDelegate.onTouchExplorationHoverEvent(event)) {
- return true;
- }
+ if (mTouchDelegate != null && dispatchTouchExplorationHoverEvent(event)) {
+ return true;
}
// The root view may receive hover (or touch) events that are outside the bounds of
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index b754d84..eb35587 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -16,302 +16,22 @@
*/
package android.widget;
-import android.annotation.UnsupportedAppUsage;
-import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.UserHandle;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import com.android.internal.R;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
/**
- * This class contains the SecurityPermissions view implementation.
- * Initially the package's advanced or dangerous security permissions
- * are displayed under categorized
- * groups. Clicking on the additional permissions presents
- * extended information consisting of all groups and permissions.
- * To use this view define a LinearLayout or any ViewGroup and add this
- * view by instantiating AppSecurityPermissions and invoking getPermissionsView.
+ * Allows the device admin to show certain dialogs. Should be integrated into settings.
*
+ * @deprecated
* {@hide}
*/
+@Deprecated
public class AppSecurityPermissions {
- public static final int WHICH_NEW = 1<<2;
- public static final int WHICH_ALL = 0xffff;
-
- private final static String TAG = "AppSecurityPermissions";
- private final static boolean localLOGV = false;
- private final Context mContext;
- private final LayoutInflater mInflater;
- private final PackageManager mPm;
- private final Map<String, MyPermissionGroupInfo> mPermGroups
- = new HashMap<String, MyPermissionGroupInfo>();
- private final List<MyPermissionGroupInfo> mPermGroupsList
- = new ArrayList<MyPermissionGroupInfo>();
- private final PermissionGroupInfoComparator mPermGroupComparator =
- new PermissionGroupInfoComparator();
- private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();
- private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>();
- private final CharSequence mNewPermPrefix;
- private String mPackageName;
-
- /** @hide */
- static class MyPermissionGroupInfo extends PermissionGroupInfo {
- CharSequence mLabel;
-
- final ArrayList<MyPermissionInfo> mNewPermissions = new ArrayList<MyPermissionInfo>();
- final ArrayList<MyPermissionInfo> mAllPermissions = new ArrayList<MyPermissionInfo>();
-
- MyPermissionGroupInfo(PermissionInfo perm) {
- name = perm.packageName;
- packageName = perm.packageName;
- }
-
- MyPermissionGroupInfo(PermissionGroupInfo info) {
- super(info);
- }
-
- public Drawable loadGroupIcon(Context context, PackageManager pm) {
- if (icon != 0) {
- return loadUnbadgedIcon(pm);
- } else {
- return context.getDrawable(R.drawable.ic_perm_device_info);
- }
- }
- }
-
- /** @hide */
- private static class MyPermissionInfo extends PermissionInfo {
- CharSequence mLabel;
-
- /**
- * PackageInfo.requestedPermissionsFlags for the new package being installed.
- */
- int mNewReqFlags;
-
- /**
- * PackageInfo.requestedPermissionsFlags for the currently installed
- * package, if it is installed.
- */
- int mExistingReqFlags;
-
- /**
- * True if this should be considered a new permission.
- */
- boolean mNew;
-
- MyPermissionInfo(PermissionInfo info) {
- super(info);
- }
- }
-
- /** @hide */
- public static class PermissionItemView extends LinearLayout implements View.OnClickListener {
- MyPermissionGroupInfo mGroup;
- MyPermissionInfo mPerm;
- AlertDialog mDialog;
- private boolean mShowRevokeUI = false;
- private String mPackageName;
-
- public PermissionItemView(Context context, AttributeSet attrs) {
- super(context, attrs);
- setClickable(true);
- }
-
- public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm,
- boolean first, CharSequence newPermPrefix, String packageName,
- boolean showRevokeUI) {
- mGroup = grp;
- mPerm = perm;
- mShowRevokeUI = showRevokeUI;
- mPackageName = packageName;
-
- ImageView permGrpIcon = findViewById(R.id.perm_icon);
- TextView permNameView = findViewById(R.id.perm_name);
-
- PackageManager pm = getContext().getPackageManager();
- Drawable icon = null;
- if (first) {
- icon = grp.loadGroupIcon(getContext(), pm);
- }
- CharSequence label = perm.mLabel;
- if (perm.mNew && newPermPrefix != null) {
- // If this is a new permission, format it appropriately.
- SpannableStringBuilder builder = new SpannableStringBuilder();
- Parcel parcel = Parcel.obtain();
- TextUtils.writeToParcel(newPermPrefix, parcel, 0);
- parcel.setDataPosition(0);
- CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
- parcel.recycle();
- builder.append(newStr);
- builder.append(label);
- label = builder;
- }
-
- permGrpIcon.setImageDrawable(icon);
- permNameView.setText(label);
- setOnClickListener(this);
- if (localLOGV) Log.i(TAG, "Made perm item " + perm.name
- + ": " + label + " in group " + grp.name);
- }
-
- @Override
- public void onClick(View v) {
- if (mGroup != null && mPerm != null) {
- if (mDialog != null) {
- mDialog.dismiss();
- }
- PackageManager pm = getContext().getPackageManager();
- AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
- builder.setTitle(mGroup.mLabel);
- if (mPerm.descriptionRes != 0) {
- builder.setMessage(mPerm.loadDescription(pm));
- } else {
- CharSequence appName;
- try {
- ApplicationInfo app = pm.getApplicationInfo(mPerm.packageName, 0);
- appName = app.loadLabel(pm);
- } catch (NameNotFoundException e) {
- appName = mPerm.packageName;
- }
- StringBuilder sbuilder = new StringBuilder(128);
- sbuilder.append(getContext().getString(
- R.string.perms_description_app, appName));
- sbuilder.append("\n\n");
- sbuilder.append(mPerm.name);
- builder.setMessage(sbuilder.toString());
- }
- builder.setCancelable(true);
- builder.setIcon(mGroup.loadGroupIcon(getContext(), pm));
- addRevokeUIIfNecessary(builder);
- mDialog = builder.show();
- mDialog.setCanceledOnTouchOutside(true);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mDialog != null) {
- mDialog.dismiss();
- }
- }
-
- private void addRevokeUIIfNecessary(AlertDialog.Builder builder) {
- if (!mShowRevokeUI) {
- return;
- }
-
- final boolean isRequired =
- ((mPerm.mExistingReqFlags & PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0);
-
- if (isRequired) {
- return;
- }
-
- DialogInterface.OnClickListener ocl = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- PackageManager pm = getContext().getPackageManager();
- pm.revokeRuntimePermission(mPackageName, mPerm.name,
- new UserHandle(mContext.getUserId()));
- PermissionItemView.this.setVisibility(View.GONE);
- }
- };
- builder.setNegativeButton(R.string.revoke, ocl);
- builder.setPositiveButton(R.string.ok, null);
- }
- }
-
- private AppSecurityPermissions(Context context) {
- mContext = context;
- mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mPm = mContext.getPackageManager();
- // Pick up from framework resources instead.
- mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix);
- }
-
- @UnsupportedAppUsage
- public AppSecurityPermissions(Context context, String packageName) {
- this(context);
- mPackageName = packageName;
- Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
- PackageInfo pkgInfo;
- try {
- pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Couldn't retrieve permissions for package:"+packageName);
- return;
- }
- // Extract all user permissions
- if((pkgInfo.applicationInfo != null) && (pkgInfo.applicationInfo.uid != -1)) {
- getAllUsedPermissions(pkgInfo.applicationInfo.uid, permSet);
- }
- mPermsList.addAll(permSet);
- setPermissions(mPermsList);
- }
-
- public AppSecurityPermissions(Context context, PackageInfo info) {
- this(context);
- Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
- if(info == null) {
- return;
- }
- mPackageName = info.packageName;
-
- // Convert to a PackageInfo
- PackageInfo installedPkgInfo = null;
- // Get requested permissions
- if (info.requestedPermissions != null) {
- try {
- installedPkgInfo = mPm.getPackageInfo(info.packageName,
- PackageManager.GET_PERMISSIONS);
- } catch (NameNotFoundException e) {
- }
- extractPerms(info, permSet, installedPkgInfo);
- }
- // Get permissions related to shared user if any
- if (info.sharedUserId != null) {
- int sharedUid;
- try {
- sharedUid = mPm.getUidForSharedUser(info.sharedUserId);
- getAllUsedPermissions(sharedUid, permSet);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Couldn't retrieve shared user id for: " + info.packageName);
- }
- }
- // Retrieve list of permissions
- mPermsList.addAll(permSet);
- setPermissions(mPermsList);
- }
-
/**
* Utility to retrieve a view displaying a single permission. This provides
* the old UI layout for permissions; it is only here for the device admin
@@ -327,197 +47,6 @@
description, dangerous, icon);
}
- private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) {
- String sharedPkgList[] = mPm.getPackagesForUid(sharedUid);
- if(sharedPkgList == null || (sharedPkgList.length == 0)) {
- return;
- }
- for(String sharedPkg : sharedPkgList) {
- getPermissionsForPackage(sharedPkg, permSet);
- }
- }
-
- private void getPermissionsForPackage(String packageName, Set<MyPermissionInfo> permSet) {
- try {
- PackageInfo pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
- extractPerms(pkgInfo, permSet, pkgInfo);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Couldn't retrieve permissions for package: " + packageName);
- }
- }
-
- private void extractPerms(PackageInfo info, Set<MyPermissionInfo> permSet,
- PackageInfo installedPkgInfo) {
- String[] strList = info.requestedPermissions;
- int[] flagsList = info.requestedPermissionsFlags;
- if ((strList == null) || (strList.length == 0)) {
- return;
- }
- for (int i=0; i<strList.length; i++) {
- String permName = strList[i];
- try {
- PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0);
- if (tmpPermInfo == null) {
- continue;
- }
- int existingIndex = -1;
- if (installedPkgInfo != null
- && installedPkgInfo.requestedPermissions != null) {
- for (int j=0; j<installedPkgInfo.requestedPermissions.length; j++) {
- if (permName.equals(installedPkgInfo.requestedPermissions[j])) {
- existingIndex = j;
- break;
- }
- }
- }
- final int existingFlags = existingIndex >= 0 ?
- installedPkgInfo.requestedPermissionsFlags[existingIndex] : 0;
- if (!isDisplayablePermission(tmpPermInfo, flagsList[i], existingFlags)) {
- // This is not a permission that is interesting for the user
- // to see, so skip it.
- continue;
- }
- final String origGroupName = tmpPermInfo.group;
- String groupName = origGroupName;
- if (groupName == null) {
- groupName = tmpPermInfo.packageName;
- tmpPermInfo.group = groupName;
- }
- MyPermissionGroupInfo group = mPermGroups.get(groupName);
- if (group == null) {
- PermissionGroupInfo grp = null;
- if (origGroupName != null) {
- grp = mPm.getPermissionGroupInfo(origGroupName, 0);
- }
- if (grp != null) {
- group = new MyPermissionGroupInfo(grp);
- } else {
- // We could be here either because the permission
- // didn't originally specify a group or the group it
- // gave couldn't be found. In either case, we consider
- // its group to be the permission's package name.
- tmpPermInfo.group = tmpPermInfo.packageName;
- group = mPermGroups.get(tmpPermInfo.group);
- if (group == null) {
- group = new MyPermissionGroupInfo(tmpPermInfo);
- }
- group = new MyPermissionGroupInfo(tmpPermInfo);
- }
- mPermGroups.put(tmpPermInfo.group, group);
- }
- final boolean newPerm = installedPkgInfo != null
- && (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0;
- MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo);
- myPerm.mNewReqFlags = flagsList[i];
- myPerm.mExistingReqFlags = existingFlags;
- // This is a new permission if the app is already installed and
- // doesn't currently hold this permission.
- myPerm.mNew = newPerm;
- permSet.add(myPerm);
- } catch (NameNotFoundException e) {
- Log.i(TAG, "Ignoring unknown permission:"+permName);
- }
- }
- }
-
- @UnsupportedAppUsage
- public int getPermissionCount() {
- return getPermissionCount(WHICH_ALL);
- }
-
- private List<MyPermissionInfo> getPermissionList(MyPermissionGroupInfo grp, int which) {
- if (which == WHICH_NEW) {
- return grp.mNewPermissions;
- } else {
- return grp.mAllPermissions;
- }
- }
-
- public int getPermissionCount(int which) {
- int N = 0;
- for (int i=0; i<mPermGroupsList.size(); i++) {
- N += getPermissionList(mPermGroupsList.get(i), which).size();
- }
- return N;
- }
-
- @UnsupportedAppUsage
- public View getPermissionsView() {
- return getPermissionsView(WHICH_ALL, false);
- }
-
- public View getPermissionsViewWithRevokeButtons() {
- return getPermissionsView(WHICH_ALL, true);
- }
-
- public View getPermissionsView(int which) {
- return getPermissionsView(which, false);
- }
-
- private View getPermissionsView(int which, boolean showRevokeUI) {
- LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
- LinearLayout displayList = permsView.findViewById(R.id.perms_list);
- View noPermsView = permsView.findViewById(R.id.no_permissions);
-
- displayPermissions(mPermGroupsList, displayList, which, showRevokeUI);
- if (displayList.getChildCount() <= 0) {
- noPermsView.setVisibility(View.VISIBLE);
- }
-
- return permsView;
- }
-
- /**
- * Utility method that displays permissions from a map containing group name and
- * list of permission descriptions.
- */
- private void displayPermissions(List<MyPermissionGroupInfo> groups,
- LinearLayout permListView, int which, boolean showRevokeUI) {
- permListView.removeAllViews();
-
- int spacing = (int)(8*mContext.getResources().getDisplayMetrics().density);
-
- for (int i=0; i<groups.size(); i++) {
- MyPermissionGroupInfo grp = groups.get(i);
- final List<MyPermissionInfo> perms = getPermissionList(grp, which);
- for (int j=0; j<perms.size(); j++) {
- MyPermissionInfo perm = perms.get(j);
- View view = getPermissionItemView(grp, perm, j == 0,
- which != WHICH_NEW ? mNewPermPrefix : null, showRevokeUI);
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- if (j == 0) {
- lp.topMargin = spacing;
- }
- if (j == grp.mAllPermissions.size()-1) {
- lp.bottomMargin = spacing;
- }
- if (permListView.getChildCount() == 0) {
- lp.topMargin *= 2;
- }
- permListView.addView(view, lp);
- }
- }
- }
-
- private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp,
- MyPermissionInfo perm, boolean first, CharSequence newPermPrefix, boolean showRevokeUI) {
- return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix,
- mPackageName, showRevokeUI);
- }
-
- private static PermissionItemView getPermissionItemView(Context context, LayoutInflater inflater,
- MyPermissionGroupInfo grp, MyPermissionInfo perm, boolean first,
- CharSequence newPermPrefix, String packageName, boolean showRevokeUI) {
- PermissionItemView permView = (PermissionItemView)inflater.inflate(
- (perm.flags & PermissionInfo.FLAG_COSTS_MONEY) != 0
- ? R.layout.app_permission_item_money : R.layout.app_permission_item,
- null);
- permView.setPermission(grp, perm, first, newPermPrefix, packageName, showRevokeUI);
- return permView;
- }
-
private static View getPermissionItemViewOld(Context context, LayoutInflater inflater,
CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) {
View permView = inflater.inflate(R.layout.app_permission_item_old, null);
@@ -536,116 +65,4 @@
}
return permView;
}
-
- private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags,
- int existingReqFlags) {
- final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- final boolean isNormal = (base == PermissionInfo.PROTECTION_NORMAL);
-
- // We do not show normal permissions in the UI.
- if (isNormal) {
- return false;
- }
-
- final boolean isDangerous = (base == PermissionInfo.PROTECTION_DANGEROUS)
- || ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_PRE23) != 0);
- final boolean isRequired =
- ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0);
- final boolean isDevelopment =
- ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0);
- final boolean wasGranted =
- ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0);
- final boolean isGranted =
- ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0);
-
- // Dangerous and normal permissions are always shown to the user if the permission
- // is required, or it was previously granted
- if (isDangerous && (isRequired || wasGranted || isGranted)) {
- return true;
- }
-
- // Development permissions are only shown to the user if they are already
- // granted to the app -- if we are installing an app and they are not
- // already granted, they will not be granted as part of the install.
- if (isDevelopment && wasGranted) {
- if (localLOGV) Log.i(TAG, "Special perm " + pInfo.name
- + ": protlevel=0x" + Integer.toHexString(pInfo.protectionLevel));
- return true;
- }
- return false;
- }
-
- private static class PermissionGroupInfoComparator implements Comparator<MyPermissionGroupInfo> {
- private final Collator sCollator = Collator.getInstance();
- @Override
- public final int compare(MyPermissionGroupInfo a, MyPermissionGroupInfo b) {
- return sCollator.compare(a.mLabel, b.mLabel);
- }
- }
-
- private static class PermissionInfoComparator implements Comparator<MyPermissionInfo> {
- private final Collator sCollator = Collator.getInstance();
- PermissionInfoComparator() {
- }
- public final int compare(MyPermissionInfo a, MyPermissionInfo b) {
- return sCollator.compare(a.mLabel, b.mLabel);
- }
- }
-
- private void addPermToList(List<MyPermissionInfo> permList,
- MyPermissionInfo pInfo) {
- if (pInfo.mLabel == null) {
- pInfo.mLabel = pInfo.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
- | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
- }
- int idx = Collections.binarySearch(permList, pInfo, mPermComparator);
- if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+permList.size());
- if (idx < 0) {
- idx = -idx-1;
- permList.add(idx, pInfo);
- }
- }
-
- private void setPermissions(List<MyPermissionInfo> permList) {
- if (permList != null) {
- // First pass to group permissions
- for (MyPermissionInfo pInfo : permList) {
- if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name);
- if(!isDisplayablePermission(pInfo, pInfo.mNewReqFlags, pInfo.mExistingReqFlags)) {
- if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");
- continue;
- }
- MyPermissionGroupInfo group = mPermGroups.get(pInfo.group);
- if (group != null) {
- pInfo.mLabel = pInfo.loadSafeLabel(mPm, 20000,
- PackageItemInfo.SAFE_LABEL_FLAG_TRIM
- | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
- addPermToList(group.mAllPermissions, pInfo);
- if (pInfo.mNew) {
- addPermToList(group.mNewPermissions, pInfo);
- }
- }
- }
- }
-
- for (MyPermissionGroupInfo pgrp : mPermGroups.values()) {
- if (pgrp.labelRes != 0 || pgrp.nonLocalizedLabel != null) {
- pgrp.mLabel = pgrp.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
- | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
- } else {
- ApplicationInfo app;
- try {
- app = mPm.getApplicationInfo(pgrp.packageName, 0);
- pgrp.mLabel = app.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
- | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
- } catch (NameNotFoundException e) {
- pgrp.mLabel = pgrp.loadSafeLabel(mPm, 20000,
- PackageItemInfo.SAFE_LABEL_FLAG_TRIM
- | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
- }
- }
- mPermGroupsList.add(pgrp);
- }
- Collections.sort(mPermGroupsList, mPermGroupComparator);
- }
}
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 0baf73c..02c9542 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -130,6 +130,10 @@
public double wakeLockPowerMah;
public double wifiPowerMah;
+ // ****************
+ // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto)
+ // so the ordinal values (and therefore the order) must never change.
+ // ****************
public enum DrainType {
AMBIENT_DISPLAY,
@UnsupportedAppUsage
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 8f87f91..8bdb000 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -173,12 +173,13 @@
}
native private static void nativePreloadAppProcessHALs();
+ native private static void nativePreloadOpenGL();
private static void preloadOpenGL() {
String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false) &&
(driverPackageName == null || driverPackageName.isEmpty())) {
- EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+ nativePreloadOpenGL();
}
}
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 8ace8da..5887fa7 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -135,13 +135,13 @@
LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
int register_android_server_InputApplicationHandle(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, "com/android/server/input/InputApplicationHandle",
+ int res = jniRegisterNativeMethods(env, "android/view/InputApplicationHandle",
gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods));
(void) res; // Faked use when LOG_NDEBUG.
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
jclass clazz;
- FIND_CLASS(clazz, "com/android/server/input/InputApplicationHandle");
+ FIND_CLASS(clazz, "android/view/InputApplicationHandle");
GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, clazz,
"ptr", "J");
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 5b72241..6ecb5de 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -221,20 +221,20 @@
LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
int register_android_server_InputWindowHandle(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, "com/android/server/input/InputWindowHandle",
+ int res = jniRegisterNativeMethods(env, "android/view/InputWindowHandle",
gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods));
(void) res; // Faked use when LOG_NDEBUG.
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
jclass clazz;
- FIND_CLASS(clazz, "com/android/server/input/InputWindowHandle");
+ FIND_CLASS(clazz, "android/view/InputWindowHandle");
GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz,
"ptr", "J");
GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle,
clazz,
- "inputApplicationHandle", "Lcom/android/server/input/InputApplicationHandle;");
+ "inputApplicationHandle", "Landroid/view/InputApplicationHandle;");
GET_FIELD_ID(gInputWindowHandleClassInfo.inputChannel, clazz,
"inputChannel", "Landroid/view/InputChannel;");
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b2d44e7..7b564ae 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -24,9 +24,13 @@
#include <sys/system_properties.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
+#include <sstream>
+#include <string>
+
#include "android-base/logging.h"
#include "android-base/properties.h"
#include "android-base/stringprintf.h"
@@ -38,6 +42,7 @@
#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeResolution.h"
#include "androidfw/MutexGuard.h"
+#include "androidfw/PosixUtils.h"
#include "androidfw/ResourceTypes.h"
#include "core_jni_helpers.h"
#include "jni.h"
@@ -54,6 +59,7 @@
extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
using ::android::base::StringPrintf;
+using ::android::util::ExecuteBinary;
namespace android {
@@ -161,18 +167,20 @@
argv[argc++] = AssetManager::IDMAP_DIR;
// Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
- // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
+ // use VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
+ // addition to VENDOR_OVERLAY_DIR.
std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY,
"");
if (!overlay_theme_path.empty()) {
- overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path;
+ overlay_theme_path =
+ std::string(AssetManager::VENDOR_OVERLAY_DIR) + "/" + overlay_theme_path;
if (stat(overlay_theme_path.c_str(), &st) == 0) {
argv[argc++] = overlay_theme_path.c_str();
}
}
- if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
- argv[argc++] = AssetManager::OVERLAY_DIR;
+ if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::VENDOR_OVERLAY_DIR;
}
if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
@@ -200,6 +208,75 @@
}
}
+static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env,
+ jclass /*clazz*/) {
+ // --input-directory can be given multiple times, but idmap2 expects the directory to exist
+ std::vector<std::string> input_dirs;
+ struct stat st;
+ if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
+ input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR);
+ }
+
+ if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
+ input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR);
+ }
+
+ if (stat(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR, &st) == 0) {
+ input_dirs.push_back(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR);
+ }
+
+ if (input_dirs.empty()) {
+ LOG(WARNING) << "no directories for idmap2 to scan";
+ return env->NewObjectArray(0, g_stringClass, nullptr);
+ }
+
+ std::vector<std::string> argv{"/system/bin/idmap2",
+ "scan",
+ "--recursive",
+ "--target-package-name", "android",
+ "--target-apk-path", "/system/framework/framework-res.apk",
+ "--output-directory", "/data/resource-cache"};
+
+ for (const auto& dir : input_dirs) {
+ argv.push_back("--input-directory");
+ argv.push_back(dir);
+ }
+
+ const auto result = ExecuteBinary(argv);
+
+ if (!result) {
+ LOG(ERROR) << "failed to execute idmap2";
+ return nullptr;
+ }
+
+ if (result->status != 0) {
+ LOG(ERROR) << "idmap2: " << result->stderr;
+ return nullptr;
+ }
+
+ std::vector<std::string> idmap_paths;
+ std::istringstream input(result->stdout);
+ std::string path;
+ while (std::getline(input, path)) {
+ idmap_paths.push_back(path);
+ }
+
+ jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < idmap_paths.size(); i++) {
+ const std::string path = idmap_paths[i];
+ jstring java_string = env->NewStringUTF(path.c_str());
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ env->SetObjectArrayElement(array, i, java_string);
+ env->DeleteLocalRef(java_string);
+ }
+ return array;
+}
+
static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
@@ -1405,6 +1482,8 @@
// System/idmap related methods.
{"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps},
+ {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;",
+ (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
// Global management/debug methods.
{"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 2f17907..fb6be6b 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -249,6 +249,24 @@
}
}
+static jobject android_view_InputChannel_nativeGetToken(JNIEnv* env, jobject obj) {
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ if (nativeInputChannel) {
+ return javaObjectForIBinder(env, nativeInputChannel->getInputChannel()->getToken());
+ }
+ return 0;
+}
+
+static void android_view_InputChannel_nativeSetToken(JNIEnv* env, jobject obj, jobject tokenObj) {
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ sp<IBinder> token = ibinderForJavaObject(env, tokenObj);
+ if (nativeInputChannel != nullptr) {
+ nativeInputChannel->getInputChannel()->setToken(token);
+ }
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputChannelMethods[] = {
@@ -267,6 +285,10 @@
(void*)android_view_InputChannel_nativeGetName },
{ "nativeDup", "(Landroid/view/InputChannel;)V",
(void*)android_view_InputChannel_nativeDup },
+ { "nativeGetToken", "()Landroid/os/IBinder;",
+ (void*)android_view_InputChannel_nativeGetToken },
+ { "nativeSetToken", "(Landroid/os/IBinder;)V",
+ (void*)android_view_InputChannel_nativeSetToken }
};
int register_android_view_InputChannel(JNIEnv* env) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 4eda3ab..ec9c860 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -19,6 +19,7 @@
#include "android_os_Parcel.h"
#include "android_util_Binder.h"
+#include "android_hardware_input_InputWindowHandle.h"
#include "android/graphics/Bitmap.h"
#include "android/graphics/GraphicsJNI.h"
#include "android/graphics/Region.h"
@@ -324,6 +325,18 @@
transaction->setAlpha(ctrl, alpha);
}
+static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jobject inputWindow) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ sp<NativeInputWindowHandle> handle = android_server_InputWindowHandle_getHandle(
+ env, inputWindow);
+ handle->updateInfo();
+
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ transaction->setInputWindowInfo(ctrl, *handle->getInfo());
+}
+
static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jfloatArray fColor) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -930,6 +943,8 @@
(void*)nativeScreenshot },
{"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
(void*)nativeCaptureLayers },
+ {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
+ (void*)nativeSetInputWindowInfo },
};
int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp
index 258a55c..ac0e600 100644
--- a/core/jni/com_android_internal_os_ZygoteInit.cpp
+++ b/core/jni/com_android_internal_os_ZygoteInit.cpp
@@ -16,21 +16,58 @@
#define LOG_TAG "Zygote"
+#include <EGL/egl.h>
#include <ui/GraphicBufferMapper.h>
#include "core_jni_helpers.h"
namespace {
+// Shadow call stack (SCS) is a security mitigation that uses a separate stack
+// (the SCS) for return addresses. In versions of Android newer than P, the
+// compiler cooperates with the system to ensure that the SCS address is always
+// stored in register x18, as long as the app was compiled with a new enough
+// compiler and does not use features that rely on SP-HALs (this restriction is
+// because the SP-HALs might not preserve x18 due to potentially having been
+// compiled with an old compiler as a consequence of Treble; it generally means
+// that the app must be a system app without a UI). This struct is used to
+// temporarily store the address on the stack while preloading the SP-HALs, so
+// that such apps can use the same zygote as everything else.
+struct ScopedSCSExit {
+#ifdef __aarch64__
+ void* scs;
+
+ ScopedSCSExit() {
+ __asm__ __volatile__("str x18, [%0]" ::"r"(&scs));
+ }
+
+ ~ScopedSCSExit() {
+ __asm__ __volatile__("ldr x18, [%0]; str xzr, [%0]" ::"r"(&scs));
+ }
+#else
+ // Silence unused variable warnings in non-SCS builds.
+ ScopedSCSExit() {}
+ ~ScopedSCSExit() {}
+#endif
+};
+
void android_internal_os_ZygoteInit_nativePreloadAppProcessHALs(JNIEnv* env, jclass) {
+ ScopedSCSExit x;
android::GraphicBufferMapper::preloadHal();
// Add preloading here for other HALs that are (a) always passthrough, and
// (b) loaded by most app processes.
}
+void android_internal_os_ZygoteInit_nativePreloadOpenGL(JNIEnv* env, jclass) {
+ ScopedSCSExit x;
+ eglGetDisplay(EGL_DEFAULT_DISPLAY);
+}
+
const JNINativeMethod gMethods[] = {
{ "nativePreloadAppProcessHALs", "()V",
(void*)android_internal_os_ZygoteInit_nativePreloadAppProcessHALs },
+ { "nativePreloadOpenGL", "()V",
+ (void*)android_internal_os_ZygoteInit_nativePreloadOpenGL },
};
} // anonymous namespace
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 2465759..a398e49 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -85,7 +85,7 @@
// See AssetManager.cpp for more details on overlay-subdir.
static const char* kOverlayDir = "/system/vendor/overlay/";
static const char* kVendorOverlayDir = "/vendor/overlay";
- static const char* kOverlaySubdir = "/system/vendor/overlay-subdir/";
+ static const char* kVendorOverlaySubdir = "/system/vendor/overlay-subdir/";
static const char* kSystemProductOverlayDir = "/system/product/overlay/";
static const char* kProductOverlayDir = "/product/overlay";
static const char* kSystemProductServicesOverlayDir = "/system/product_services/overlay/";
@@ -93,7 +93,7 @@
static const char* kApkSuffix = ".apk";
if ((android::base::StartsWith(path, kOverlayDir)
- || android::base::StartsWith(path, kOverlaySubdir)
+ || android::base::StartsWith(path, kVendorOverlaySubdir)
|| android::base::StartsWith(path, kVendorOverlayDir)
|| android::base::StartsWith(path, kSystemProductOverlayDir)
|| android::base::StartsWith(path, kProductOverlayDir)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2976879..62896be 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -746,7 +746,7 @@
android:description="@string/permdesc_receiveMms"
android:protectionLevel="dangerous" />
- <!-- @TestApi Allows an application to read previously received cell broadcast
+ <!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
messages and to register a content observer to get notifications when
a cell broadcast has been received and added to the database. For
emergency alerts, the database is updated immediately after the
diff --git a/core/res/res/values/colors_car.xml b/core/res/res/values/colors_car.xml
index 32671ac8..ea7c009 100644
--- a/core/res/res/values/colors_car.xml
+++ b/core/res/res/values/colors_car.xml
@@ -284,4 +284,8 @@
<color name="car_red_500a">#ffd50000</color>
<color name="car_red_a700">#ffd50000</color>
+
+ <color name="car_keyboard_divider_line">#38ffffff</color>
+ <color name="car_keyboard_text_primary_color">@color/car_grey_50</color>
+ <color name="car_keyboard_text_secondary_color">#8af8f9fa</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 26f3370..829d6f5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -55,6 +55,8 @@
<item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
</string-array>
@@ -87,6 +89,8 @@
<string translatable="false" name="status_bar_mobile">mobile</string>
<string translatable="false" name="status_bar_vpn">vpn</string>
<string translatable="false" name="status_bar_ethernet">ethernet</string>
+ <string translatable="false" name="status_bar_microphone">microphone</string>
+ <string translatable="false" name="status_bar_camera">camera</string>
<string translatable="false" name="status_bar_airplane">airplane</string>
<!-- Flag indicating whether the surface flinger has limited
diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml
index c1ca33e..5014a29 100644
--- a/core/res/res/values/dimens_car.xml
+++ b/core/res/res/values/dimens_car.xml
@@ -34,7 +34,6 @@
<!-- The diff between keyline 1 and keyline 3. -->
<dimen name="car_keyline_1_keyline_3_diff">88dp</dimen>
<dimen name="car_dialog_action_bar_height">@dimen/car_card_action_bar_height</dimen>
- <dimen name="car_primary_icon_size">44dp</dimen>
<!-- Text size for car -->
<dimen name="car_title_size">32sp</dimen>
@@ -56,16 +55,19 @@
<!-- Common icon size for car app -->
<dimen name="car_icon_size">56dp</dimen>
+ <dimen name="car_primary_icon_size">44dp</dimen>
+ <dimen name="car_secondary_icon_size">36dp</dimen>
- <dimen name="car_card_header_height">96dp</dimen>
- <dimen name="car_card_action_bar_height">96dp</dimen>
+ <dimen name="car_card_header_height">76dp</dimen>
+ <dimen name="car_card_action_bar_height">76dp</dimen>
<!-- Paddings -->
- <dimen name="car_padding_1">4dp</dimen>
- <dimen name="car_padding_2">10dp</dimen>
- <dimen name="car_padding_3">16dp</dimen>
- <dimen name="car_padding_4">28dp</dimen>
- <dimen name="car_padding_5">32dp</dimen>
+ <dimen name="car_padding_0">4dp</dimen>
+ <dimen name="car_padding_1">8dp</dimen>
+ <dimen name="car_padding_2">16dp</dimen>
+ <dimen name="car_padding_3">28dp</dimen>
+ <dimen name="car_padding_4">32dp</dimen>
+ <dimen name="car_padding_5">64dp</dimen>
<!-- Radius -->
<dimen name="car_radius_1">4dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4eb723e..626206b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2808,6 +2808,8 @@
<java-symbol type="string" name="status_bar_mobile" />
<java-symbol type="string" name="status_bar_ethernet" />
<java-symbol type="string" name="status_bar_vpn" />
+ <java-symbol type="string" name="status_bar_microphone" />
+ <java-symbol type="string" name="status_bar_camera" />
<!-- Locale picker -->
<java-symbol type="id" name="locale_search_menu" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 8c91c37..002b6a8 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -494,6 +494,7 @@
Settings.Global.WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED,
Settings.Global.WIFI_LINK_SPEED_METRICS_ENABLED,
Settings.Global.WIFI_PNO_FREQUENCY_CULLING_ENABLED,
+ Settings.Global.WIFI_PNO_RECENCY_SORTING_ENABLED,
Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
Settings.Global.WIFI_NETWORK_SHOW_RSSI,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index e81f678..7467114 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -41,6 +41,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.format.DateUtils;
+import android.util.StatsLog;
import junit.framework.TestCase;
@@ -258,6 +259,36 @@
assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS);
}
+ @Test
+ public void testDrainTypesSyncedWithProto() {
+ assertEquals(BatterySipper.DrainType.AMBIENT_DISPLAY.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__AMBIENT_DISPLAY);
+ // AtomsProto has no "APP"
+ assertEquals(BatterySipper.DrainType.BLUETOOTH.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__BLUETOOTH);
+ assertEquals(BatterySipper.DrainType.CAMERA.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CAMERA);
+ assertEquals(BatterySipper.DrainType.CELL.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CELL);
+ assertEquals(BatterySipper.DrainType.FLASHLIGHT.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__FLASHLIGHT);
+ assertEquals(BatterySipper.DrainType.IDLE.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__IDLE);
+ assertEquals(BatterySipper.DrainType.MEMORY.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__MEMORY);
+ assertEquals(BatterySipper.DrainType.OVERCOUNTED.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__OVERCOUNTED);
+ assertEquals(BatterySipper.DrainType.PHONE.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__PHONE);
+ assertEquals(BatterySipper.DrainType.SCREEN.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__SCREEN);
+ assertEquals(BatterySipper.DrainType.UNACCOUNTED.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__UNACCOUNTED);
+ // AtomsProto has no "USER"
+ assertEquals(BatterySipper.DrainType.WIFI.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__WIFI);
+ }
+
private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
int uidCode, boolean isUidNull) {
final BatterySipper sipper = mock(BatterySipper.class);
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 68f24fb..a4c5ed2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -173,6 +173,7 @@
<assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
<assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
+ <assign-permission name="android.permission.BATTERY_STATS" uid="statsd" />
<assign-permission name="android.permission.DUMP" uid="statsd" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="statsd" />
<assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index d3c8782..835b735 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -54,6 +54,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
+import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
@@ -307,6 +308,31 @@
}
}
+ /**
+ * List uids of all keys that are auth bound to the current user.
+ * Only system is allowed to call this method.
+ */
+ @UnsupportedAppUsage
+ public int[] listUidsOfAuthBoundKeys() {
+ final int MAX_RESULT_SIZE = 100;
+ int[] uidsOut = new int[MAX_RESULT_SIZE];
+ try {
+ int rc = mBinder.listUidsOfAuthBoundKeys(uidsOut);
+ if (rc != NO_ERROR) {
+ Log.w(TAG, String.format("listUidsOfAuthBoundKeys failed with error code %d", rc));
+ return null;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ return null;
+ } catch (android.os.ServiceSpecificException e) {
+ Log.w(TAG, "KeyStore exception", e);
+ return null;
+ }
+ // Remove any 0 entries
+ return Arrays.stream(uidsOut).filter(x -> x > 0).toArray();
+ }
+
public String[] list(String prefix) {
return list(prefix, UID_SELF);
}
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 843c146..1cb0d25 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -72,7 +72,7 @@
const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
-const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
+const char* AssetManager::VENDOR_OVERLAY_DIR = "/vendor/overlay";
const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay";
const char* AssetManager::PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay";
const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index cdb87bc..e22e2d2 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -59,13 +59,13 @@
public:
static const char* RESOURCES_FILENAME;
static const char* IDMAP_BIN;
- static const char* OVERLAY_DIR;
+ static const char* VENDOR_OVERLAY_DIR;
static const char* PRODUCT_OVERLAY_DIR;
static const char* PRODUCT_SERVICES_OVERLAY_DIR;
/*
* If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay
- * APKs in OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to
- * OVERLAY_DIR.
+ * APKs in VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
+ * addition to VENDOR_OVERLAY_DIR.
*/
static const char* OVERLAY_THEME_DIR_PROPERTY;
static const char* TARGET_PACKAGE_NAME;
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index a1a6d9f..04c4629 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -85,9 +85,6 @@
case StatsLogValue::FLOAT:
mElements.push_back(StatsLogValue(in->readFloat()));
break;
- case StatsLogValue::DOUBLE:
- mElements.push_back(StatsLogValue(in->readDouble()));
- break;
case StatsLogValue::STORAGE:
mElements.push_back(StatsLogValue());
mElements.back().setType(StatsLogValue::STORAGE);
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index 1cc650b..823af65 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -18,7 +18,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.icu.util.ULocale;
import java.lang.annotation.Retention;
@@ -172,6 +171,10 @@
return localeLabels;
}
+ private Map<ULocale, String> getULabels() {
+ return mLabels;
+ }
+
/**
* @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code.
*/
@@ -231,17 +234,24 @@
AudioPresentation obj = (AudioPresentation) o;
return mPresentationId == obj.getPresentationId()
&& mProgramId == obj.getProgramId()
- && mLanguage == obj.getULocale()
+ && mLanguage.equals(obj.getULocale())
&& mMasteringIndication == obj.getMasteringIndication()
&& mAudioDescriptionAvailable == obj.hasAudioDescription()
&& mSpokenSubtitlesAvailable == obj.hasSpokenSubtitles()
&& mDialogueEnhancementAvailable == obj.hasDialogueEnhancement()
- && mLabels.equals(obj.getLabels());
+ && mLabels.equals(obj.getULabels());
}
@Override
public int hashCode() {
- return Objects.hashCode(mPresentationId);
+ return Objects.hash(mPresentationId,
+ mProgramId,
+ mLanguage.hashCode(),
+ mMasteringIndication,
+ mAudioDescriptionAvailable,
+ mSpokenSubtitlesAvailable,
+ mDialogueEnhancementAvailable,
+ mLabels.hashCode());
}
/**
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 24b7f36..cdbc7b44 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -418,9 +418,6 @@
/**
* Returns the status code for the key
- * @return one of {@link #STATUS_USABLE}, {@link #STATUS_EXPIRED},
- * {@link #STATUS_OUTPUT_NOT_ALLOWED}, {@link #STATUS_PENDING}
- * or {@link #STATUS_INTERNAL_ERROR}.
*/
@KeyStatusCode
public int getStatusCode() { return mStatusCode; }
@@ -654,13 +651,7 @@
* can be queried using {@link #getSecurityLevel}. A session
* ID is returned.
*
- * @param level the new security level, one of
- * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO},
- * {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
- * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO},
- * {@link #SECURITY_LEVEL_HW_SECURE_DECODE} or
- * {@link #SECURITY_LEVEL_HW_SECURE_ALL}.
- *
+ * @param level the new security level
* @throws NotProvisionedException if provisioning is needed
* @throws ResourceBusyException if required resources are in use
* @throws IllegalArgumentException if the requested security level is
@@ -790,9 +781,6 @@
/**
* Get the type of the request
- * @return one of {@link #REQUEST_TYPE_INITIAL},
- * {@link #REQUEST_TYPE_RENEWAL}, {@link #REQUEST_TYPE_RELEASE},
- * {@link #REQUEST_TYPE_NONE} or {@link #REQUEST_TYPE_UPDATE}
*/
@RequestType
public int getRequestType() { return mRequestType; }
@@ -1051,8 +1039,7 @@
* an inactive offline license are not usable for decryption.
*
* @param keySetId selects the offline license
- * @return the offline license state, one of {@link #OFFLINE_LICENSE_USABLE},
- * {@link #OFFLINE_LICENSE_INACTIVE} or {@link #OFFLINE_LICENSE_STATE_UNKNOWN}.
+ * @return the offline license state
* @throws IllegalArgumentException if the keySetId does not refer to an
* offline license.
*/
@@ -1191,9 +1178,7 @@
* enforcing compliance with HDCP requirements. Trusted enforcement of
* HDCP policies must be handled by the DRM system.
* <p>
- * @return one of {@link #HDCP_LEVEL_UNKNOWN}, {@link #HDCP_NONE},
- * {@link #HDCP_V1}, {@link #HDCP_V2}, {@link #HDCP_V2_1}, {@link #HDCP_V2_2}
- * or {@link #HDCP_NO_DIGITAL_OUTPUT}.
+ * @return the connected HDCP level
*/
@HdcpLevel
public native int getConnectedHdcpLevel();
@@ -1204,9 +1189,7 @@
* that may be connected. If multiple HDCP-capable interfaces are present,
* it indicates the highest of the maximum HDCP levels of all interfaces.
* <p>
- * @return one of {@link #HDCP_LEVEL_UNKNOWN}, {@link #HDCP_NONE},
- * {@link #HDCP_V1}, {@link #HDCP_V2}, {@link #HDCP_V2_1}, {@link #HDCP_V2_2}
- * or {@link #HDCP_NO_DIGITAL_OUTPUT}.
+ * @return the maximum supported HDCP level
*/
@HdcpLevel
public native int getMaxHdcpLevel();
@@ -1296,10 +1279,7 @@
* time a session is opened using {@link #openSession}.
* @param sessionId the session to query.
* <p>
- * @return one of {@link #SECURITY_LEVEL_UNKNOWN},
- * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO}, {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
- * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO}, {@link #SECURITY_LEVEL_HW_SECURE_DECODE} or
- * {@link #SECURITY_LEVEL_HW_SECURE_ALL}.
+ * @return the security level of the session
*/
@SecurityLevel
public native int getSecurityLevel(@NonNull byte[] sessionId);
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 4919eeb..c203fa9 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -272,10 +272,12 @@
public static final class CasInfo {
private final int mSystemId;
private final MediaCas.Session mSession;
+ private final byte[] mPrivateData;
- CasInfo(int systemId, @Nullable MediaCas.Session session) {
+ CasInfo(int systemId, @Nullable MediaCas.Session session, @Nullable byte[] privateData) {
mSystemId = systemId;
mSession = session;
+ mPrivateData = privateData;
}
/**
@@ -288,10 +290,30 @@
}
/**
+ * Retrieves the private data in the CA_Descriptor associated with a track.
+ * Some CAS systems may need this to initialize the CAS plugin object. This
+ * private data can only be retrieved before a valid {@link MediaCas} object
+ * is set on the extractor.
+ * <p>
+ * @see MediaExtractor#setMediaCas
+ * <p>
+ * @return a byte array containing the private data. A null return value
+ * indicates that the private data is unavailable. An empty array,
+ * on the other hand, indicates that the private data is empty
+ * (zero in length).
+ */
+ @Nullable
+ public byte[] getPrivateData() {
+ return mPrivateData;
+ }
+
+ /**
* Retrieves the {@link MediaCas.Session} associated with a track. The
* session is needed to initialize a descrambler in order to decode the
- * scrambled track.
+ * scrambled track. The session object can only be retrieved after a valid
+ * {@link MediaCas} object is set on the extractor.
* <p>
+ * @see MediaExtractor#setMediaCas
* @see MediaDescrambler#setMediaCasSession
* <p>
* @return a {@link MediaCas.Session} object associated with a track.
@@ -321,6 +343,13 @@
if (formatMap.containsKey(MediaFormat.KEY_CA_SYSTEM_ID)) {
int systemId = ((Integer)formatMap.get(MediaFormat.KEY_CA_SYSTEM_ID)).intValue();
MediaCas.Session session = null;
+ byte[] privateData = null;
+ if (formatMap.containsKey(MediaFormat.KEY_CA_PRIVATE_DATA)) {
+ ByteBuffer buf = (ByteBuffer) formatMap.get(MediaFormat.KEY_CA_PRIVATE_DATA);
+ buf.rewind();
+ privateData = new byte[buf.remaining()];
+ buf.get(privateData);
+ }
if (mMediaCas != null && formatMap.containsKey(MediaFormat.KEY_CA_SESSION_ID)) {
ByteBuffer buf = (ByteBuffer) formatMap.get(MediaFormat.KEY_CA_SESSION_ID);
buf.rewind();
@@ -328,7 +357,7 @@
buf.get(sessionId);
session = mMediaCas.createFromSessionId(toByteArray(sessionId));
}
- return new CasInfo(systemId, session);
+ return new CasInfo(systemId, session, privateData);
}
return null;
}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index d10cbbc..5dee16e 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -919,7 +919,7 @@
* a media track.
* <p>
* This key is set by {@link MediaExtractor} if the track is scrambled with a conditional
- * access system.
+ * access system, regardless of the presence of a valid {@link MediaCas} object.
* <p>
* The associated value is an integer.
* @hide
@@ -930,13 +930,25 @@
* A key describing the {@link MediaCas.Session} object associated with a media track.
* <p>
* This key is set by {@link MediaExtractor} if the track is scrambled with a conditional
- * access system.
+ * access system, after it receives a valid {@link MediaCas} object.
* <p>
* The associated value is a ByteBuffer.
* @hide
*/
public static final String KEY_CA_SESSION_ID = "ca-session-id";
+
+ /**
+ * A key describing the private data in the CA_descriptor associated with a media track.
+ * <p>
+ * This key is set by {@link MediaExtractor} if the track is scrambled with a conditional
+ * access system, before it receives a valid {@link MediaCas} object.
+ * <p>
+ * The associated value is a ByteBuffer.
+ * @hide
+ */
+ public static final String KEY_CA_PRIVATE_DATA = "ca-private-data";
+
/* package private */ MediaFormat(Map<String, Object> map) {
mMap = map;
}
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 9426148..942eafd 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -37,6 +37,7 @@
ldflags: ["-Wl,--hash-style=both"],
},
},
+ version_script: "libjnigraphics.map.txt",
}
// The headers module is in frameworks/native/Android.bp.
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index 9ecaa03..5ab6632 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -26,7 +26,8 @@
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<application android:label="@string/app_name"
- android:usesCleartextTraffic="true">
+ android:usesCleartextTraffic="true"
+ android:supportsRtl="true" >
<activity
android:name="com.android.captiveportallogin.CaptivePortalLoginActivity"
android:label="@string/action_bar_label"
diff --git a/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml b/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml
new file mode 100644
index 0000000..d460041
--- /dev/null
+++ b/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/ssl_error_msg"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_marginStart="20dip"
+ android:layout_marginEnd="20dip"
+ android:gravity="center_vertical"
+ android:layout_marginBottom="4dip"
+ android:layout_marginTop="4dip" />
+
diff --git a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
new file mode 100644
index 0000000..ffd57a4
--- /dev/null
+++ b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <!-- ssl error type -->
+ <TextView
+ android:id="@+id/ssl_error_type"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:text="SSL_UNKNOWN"
+ android:layout_marginStart="24dip"
+ android:layout_marginEnd="24dip"
+ android:layout_marginBottom="0dip"
+ android:layout_marginTop="24dip" />
+
+ <!-- Page info: -->
+ <TextView
+ android:id="@+id/page_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/page_info"
+ android:textStyle="bold"
+ android:layout_marginStart="24dip"
+ android:layout_marginEnd="24dip" />
+
+ <!-- Title: -->
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:layout_marginStart="24dip"
+ android:layout_marginEnd="24dip" />
+
+ <!-- Address: -->
+ <TextView
+ android:id="@+id/address_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/page_info_address"
+ android:layout_marginStart="24dip"
+ android:layout_marginEnd="24dip" />
+
+ <TextView
+ android:id="@+id/address"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dip"
+ android:layout_marginEnd="24dip" />
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="4dip"
+ android:paddingEnd="4dip" >
+
+ <!-- certificate view: -->
+ <LinearLayout
+ android:id="@+id/certificate_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dip" >
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/packages/CaptivePortalLogin/res/values-af/strings.xml b/packages/CaptivePortalLogin/res/values-af/strings.xml
index fa6f3fa..cf4dc82 100644
--- a/packages/CaptivePortalLogin/res/values-af/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-af/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Die netwerk waarby jy probeer aansluit, het sekuriteitkwessies."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Byvoorbeeld, die aanmeldbladsy behoort dalk nie aan die organisasie wat gewys word nie."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Gaan in elk geval deur blaaier voort"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Bladsy-inligting"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sekuriteitswaarskuwing"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Bekyk sertifikaat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Hierdie sertifikaat is nie van \'n betroubare owerheid nie."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Die naam van die werf kom nie ooreen met die naam op die sertifikaat nie."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Hierdie sertifikaat het verval."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Hierdie sertifikaat is nog nie geldig nie."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Hierdie sertifikaat het \'n ongeldige datum."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Hierdie sertifikaat is ongeldig."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Onbekende sertifikaatfout."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-am/strings.xml b/packages/CaptivePortalLogin/res/values-am/strings.xml
index 36d5e19..cdcb5a5 100644
--- a/packages/CaptivePortalLogin/res/values-am/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-am/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"ለመቀላቀል እየሞከሩ ያሉት አውታረ መረብ የደህንነት ችግሮች አሉበት።"</string>
<string name="ssl_error_example" msgid="647898534624078900">"ለምሳሌ፣ የመግቢያ ገጹ የሚታየው ድርጅት ላይሆን ይችላል።"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ለማንኛውም በአሳሽ በኩል ይቀጥሉ"</string>
+ <string name="ok" msgid="1509280796718850364">"እሺ"</string>
+ <string name="page_info" msgid="4048529256302257195">"የገፅ መረጃ"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"አድራሻ:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"የደህንነት ቅንብሮች"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"ምስክሮች ይመልከቱ"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"ይህ ምስክር ከታማኝ ቦታ አይደለም።"</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"የጣቢያው ስም ከምስክር ወረቀቱ ስም ጋር አይዛመድም።"</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"ይህ ምስክር ጊዜው አልፏል"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"ይህ ምስክር ገና ትክክል አይደለም።"</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"ይህ ምስክር ትክክለኛ ቀን አለው።"</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"ይህ ምስክር ትክክል ያልሆነ ነው።"</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"ያልታወቀ የምስክር ስህተት።"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ar/strings.xml b/packages/CaptivePortalLogin/res/values-ar/strings.xml
index 8eb259b..7773eeb 100644
--- a/packages/CaptivePortalLogin/res/values-ar/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ar/strings.xml
@@ -11,4 +11,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"الشبكة التي تحاول الانضمام إليها بها مشاكل أمنية."</string>
<string name="ssl_error_example" msgid="647898534624078900">"على سبيل المثال، قد لا تنتمي صفحة تسجيل الدخول إلى المنظمة المعروضة."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"المتابعة على أي حال عبر المتصفح"</string>
+ <string name="ok" msgid="1509280796718850364">"موافق"</string>
+ <string name="page_info" msgid="4048529256302257195">"معلومات الصفحة"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"العنوان:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"تحذير أمان"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"عرض الشهادة"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"هذه الشهادة ليست من جهة موثوق بها."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"لا يتطابق اسم الموقع مع الاسم على الشهادة."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"انتهت صلاحية هذه الشهادة."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"هذه الشهادة ليست صالحة بعد."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"تشتمل هذه الشهادة على تاريخ غير صالح."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"هذه الشهادة غير صالحة."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"حدث خطأ غير معروف بالشهادة."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-bg/strings.xml b/packages/CaptivePortalLogin/res/values-bg/strings.xml
index 8ce9deb..4dd8aa0 100644
--- a/packages/CaptivePortalLogin/res/values-bg/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-bg/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Мрежата, към която опитвате да се присъедините, има проблеми със сигурността."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Например страницата за вход може да не принадлежи на показаната организация."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Продължаване през браузър въпреки това"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Данни за страницата"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Адрес:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Предупреждение относно защитата"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Преглед на сертификата"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Сертификатът не е от надежден орган."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Името на сайта не съответства на името в сертификата."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Сертификатът е изтекъл."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Сертификатът още не е валиден."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Този сертификат е с невалидна дата."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Този сертификат е невалиден."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Неизвестна грешка в сертификата."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-bn/strings.xml b/packages/CaptivePortalLogin/res/values-bn/strings.xml
index b75d76e..fb703cf 100644
--- a/packages/CaptivePortalLogin/res/values-bn/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-bn/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"আপনি যে নেটওয়ার্কে যোগ দেওয়ার চেষ্টা করছেন তাতে নিরাপত্তার সমস্যা আছে।"</string>
<string name="ssl_error_example" msgid="647898534624078900">"উদাহরণস্বরূপ, লগ-ইন পৃষ্ঠাটি প্রদর্শিত প্রতিষ্ঠানের অন্তর্গত নাও হতে পারে৷"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"যাই হোক না কেন ব্রাউজারের মাধ্যমে অবিরত রাখুন"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Sideinfo"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhetsadvarsel"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis sertifikat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikatet er ikke fra en pålitelig myndighet."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikatet er utløpt."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikatet er ikke gyldig ennå."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette sertifikatet har en ugyldig dato."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette sertifikatet er ugyldig."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukjent sertifikatfeil."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ca/strings.xml b/packages/CaptivePortalLogin/res/values-ca/strings.xml
index fe189ed..a2c9ed8 100644
--- a/packages/CaptivePortalLogin/res/values-ca/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ca/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"La xarxa a què et vols connectar té problemes de seguretat."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Per exemple, la pàgina d\'inici de sessió podria no pertànyer a l\'organització que es mostra."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continua igualment mitjançant el navegador"</string>
+ <string name="ok" msgid="1509280796718850364">"D\'acord"</string>
+ <string name="page_info" msgid="4048529256302257195">"Informació de la pàgina"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adreça:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertiment de seguretat"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualitza el certificat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Aquest certificat no és d\'una autoritat de confiança."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nom del lloc no coincideix amb el del certificat."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Aquest certificat ha caducat."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Aquest certificat encara no és vàlid."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Aquest certificat té una data no vàlida."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Aquest certificat no és vàlid."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificat desconegut."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-cs/strings.xml b/packages/CaptivePortalLogin/res/values-cs/strings.xml
index 09dcc5f..be649a5 100644
--- a/packages/CaptivePortalLogin/res/values-cs/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-cs/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Síť, ke které se pokoušíte připojit, má bezpečnostní problémy."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Například přihlašovací stránka nemusí patřit do zobrazované organizace."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Přesto pokračovat prostřednictvím prohlížeče"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Informace o stránce"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozornění zabezpečení"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zobrazit certifikát"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Tento certifikát nepochází od důvěryhodné autority."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Název webu se neshoduje s názvem uvedeným v certifikátu."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Platnost certifikátu vypršela."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Tento certifikát ještě není platný."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Datum tohoto certifikátu není platné."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Tento certifikát je neplatný."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznámá chyba certifikátu."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-da/strings.xml b/packages/CaptivePortalLogin/res/values-da/strings.xml
index dc0dd17..8183105 100644
--- a/packages/CaptivePortalLogin/res/values-da/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-da/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Der er sikkerhedsproblemer på det netværk, du forsøger at logge ind på."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Det er f.eks. ikke sikkert, at loginsiden tilhører den anførte organisation."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Fortsæt alligevel via browseren"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Sideoplysninger"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhedsadvarsel"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis certifikat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dette certifikat stammer ikke fra en troværdig autoritet."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på websitet stemmer ikke overens med navnet på certifikatet."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Dette certifikat er udløbet."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dette certifikat er endnu ikke gyldigt."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette certifikat har en ugyldig dato."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette certifikat er ugyldigt."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukendt fejl i certifikatet."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-de/strings.xml b/packages/CaptivePortalLogin/res/values-de/strings.xml
index d8f7be9..a9b7415 100644
--- a/packages/CaptivePortalLogin/res/values-de/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-de/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Im Netzwerk, zu dem du eine Verbindung herstellen möchtest, liegen Sicherheitsprobleme vor."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Beispiel: Die Log-in-Seite gehört eventuell nicht zur angezeigten Organisation."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Trotzdem in einem Browser fortfahren"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Seiteninfo"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sicherheitswarnung"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zertifikat ansehen"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dieses Zertifikat wurde nicht von einer vertrauenswürdigen Stelle ausgegeben."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Name der Website stimmt nicht mit dem Namen auf dem Zertifikat überein."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Dieses Zertifikat ist abgelaufen."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dieses Zertifikat ist noch nicht gültig."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dieses Zertifikat weist ein ungültiges Datum auf."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Dieses Zertifikat ist ungültig."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Unbekannter Zertifikatfehler"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-el/strings.xml b/packages/CaptivePortalLogin/res/values-el/strings.xml
index cb61710..16bf6e2 100644
--- a/packages/CaptivePortalLogin/res/values-el/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-el/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Παρουσιάζονται προβλήματα ασφάλειας στο δίκτυο στο οποίο προσπαθείτε να συνδεθείτε."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Για παράδειγμα, η σελίδα σύνδεσης ενδέχεται να μην ανήκει στον οργανισμό που εμφανίζεται."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Συνέχεια ούτως ή άλλως μέσω του προγράμματος περιήγησης"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Πληροφορίες σελίδας"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Διεύθυνση:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Προειδοποίηση ασφαλείας"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Προβολή πιστοποιητικού"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Αυτό το πιστοποιητικό δεν προέρχεται από αξιόπιστη αρχή."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Το όνομα του ιστότοπου δεν αντιστοιχεί με το όνομα στο πιστοποιητικό."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Αυτό το πιστοποιητικό έχει λήξει."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Αυτό το πιστοποιητικό δεν είναι έγκυρο ακόμα."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Αυτό το πιστοποιητικό δεν έχει έγκυρη ημερομηνία."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Αυτό το πιστοποιητικό δεν είναι έγκυρο."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Άγνωστο σφάλμα πιστοποιητικού."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
index 2e8d1f0..f940299 100644
--- a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
<string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Page info"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Address:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Security warning"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"View certificate"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"This certificate isn\'t from a trusted authority."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"The name of the site doesn\'t match the name on the certificate."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"This certificate has expired."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"This certificate isn\'t valid yet."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"This certificate has an invalid date."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"This certificate is invalid."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Unknown certificate error."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
index 2e8d1f0..f940299 100644
--- a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
<string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Page info"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Address:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Security warning"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"View certificate"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"This certificate isn\'t from a trusted authority."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"The name of the site doesn\'t match the name on the certificate."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"This certificate has expired."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"This certificate isn\'t valid yet."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"This certificate has an invalid date."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"This certificate is invalid."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Unknown certificate error."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml b/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
index 5d7ba91..c011664 100644
--- a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"La red a la que intentas conectarte tiene problemas de seguridad."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Por ejemplo, es posible que la página de acceso no pertenezca a la organización que aparece."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuar de todos modos desde el navegador"</string>
+ <string name="ok" msgid="1509280796718850364">"Aceptar"</string>
+ <string name="page_info" msgid="4048529256302257195">"Información de la página"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Dirección:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertencia de seguridad"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado no proviene de una autoridad confiable."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nombre del sitio no coincide con el nombre del certificado."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado ha expirado."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado aún no es válido."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La fecha de este certificado no es válida."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado no es válido."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificado desconocido"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-es/strings.xml b/packages/CaptivePortalLogin/res/values-es/strings.xml
index da2eae9..65244e7 100644
--- a/packages/CaptivePortalLogin/res/values-es/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-es/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"La red a la que intentas unirte tiene problemas de seguridad."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Por ejemplo, es posible que la página de inicio de sesión no pertenezca a la organización mostrada."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuar de todos modos a través del navegador"</string>
+ <string name="ok" msgid="1509280796718850364">"Aceptar"</string>
+ <string name="page_info" msgid="4048529256302257195">"Información de la página"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Dirección:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertencia de seguridad"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado no procede de una entidad de certificación de confianza."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nombre del sitio no coincide con el del certificado."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado ha caducado."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado aún no es válido."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La fecha de este certificado no es válida."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado no es válido."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificado desconocido"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-et/strings.xml b/packages/CaptivePortalLogin/res/values-et/strings.xml
index 41fcb9a..e4c4c98 100644
--- a/packages/CaptivePortalLogin/res/values-et/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-et/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Võrgul, millega üritate ühenduse luua, on turvaprobleeme."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Näiteks ei pruugi sisselogimisleht kuuluda kuvatavale organisatsioonile."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Jätka siiski brauseris"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Lehe teave"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Aadress:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Turvahoiatus"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Kuva sertifikaat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"See sertifikaat ei pärine usaldusväärselt asutuselt."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Saidi nimi ei vasta sertifikaadil olevale nimele."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"See sertifikaat on aegunud."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"See sertifikaat pole veel kehtiv."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Sellel sertifikaadil on kehtetu kuupäev."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"See sertifikaat on kehtetu."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Tundmatu sertifikaadiviga."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fa/strings.xml b/packages/CaptivePortalLogin/res/values-fa/strings.xml
index 2e4cc51..27b9b7f 100644
--- a/packages/CaptivePortalLogin/res/values-fa/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-fa/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"شبکهای که میخواهید به آن بپیوندید مشکلات امنیتی دارد."</string>
<string name="ssl_error_example" msgid="647898534624078900">"به عنوان مثال، صفحه ورود به سیستم ممکن است متعلق به سازمان نشان داده شده نباشد."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"در هر صورت از طریق مرورگر ادامه یابد"</string>
+ <string name="ok" msgid="1509280796718850364">"تأیید"</string>
+ <string name="page_info" msgid="4048529256302257195">"اطلاعات صفحه"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"آدرس:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"اخطار امنیتی"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"مشاهده گواهی"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"این گواهی از یک منبع مورد اطمینان صادر نشده است."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"نام سایت با نام موجود در گواهی مطابقت ندارد."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"این گواهی منقضی شده است."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"این گواهی هنوز معتبر نیست."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"تاریخ این گواهی نامعتبر است."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"این گواهی نامعتبر است."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"خطای ناشناخته در گواهی."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fi/strings.xml b/packages/CaptivePortalLogin/res/values-fi/strings.xml
index 1976f7d..8086fbf 100644
--- a/packages/CaptivePortalLogin/res/values-fi/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-fi/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Verkossa, johon yrität muodostaa yhteyttä, on turvallisuusongelmia."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Kirjautumissivu ei välttämättä kuulu näytetylle organisaatiolle."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Jatka silti selaimen kautta."</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Sivun tiedot"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Osoite:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Suojausvaroitus"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Näytä varmenne"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Varmenteen myöntäjä ei ole luotettava taho."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Sivuston nimi ei vastaa varmenteessa olevaa nimeä."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Varmenne ei ole enää voimassa."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Varmenne ei ole vielä voimassa."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Varmenteen päiväys ei kelpaa."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Varmenne on virheellinen."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Tuntematon varmennevirhe."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fr/strings.xml b/packages/CaptivePortalLogin/res/values-fr/strings.xml
index 8f98bb5..39fc569 100644
--- a/packages/CaptivePortalLogin/res/values-fr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-fr/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Le réseau que vous essayez de rejoindre présente des problèmes de sécurité."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Par exemple, la page de connexion peut ne pas appartenir à l\'organisation représentée."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuer quand même dans le navigateur"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Infos sur la page"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adresse :"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avertissement de sécurité"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Afficher le certificat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ce certificat provient d\'une autorité non approuvée."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Le nom du site ne correspond pas au nom indiqué dans le certificat."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Le certificat a expiré."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Ce certificat n\'est pas encore valide."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La date de ce certificat n\'est pas valide."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Ce certificat n\'est pas valide."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Erreur : Certificat inconnu."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hi/strings.xml b/packages/CaptivePortalLogin/res/values-hi/strings.xml
index 1bacc46..d924fff 100644
--- a/packages/CaptivePortalLogin/res/values-hi/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-hi/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"आप जिस नेटवर्क में शामिल होने का प्रयास कर रहे हैं उसमें सुरक्षा समस्याएं हैं."</string>
<string name="ssl_error_example" msgid="647898534624078900">"उदाहरण के लिए, हो सकता है कि लॉगिन पृष्ठ दिखाए गए संगठन से संबद्ध ना हो."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ब्राउज़र के द्वारा फिर जारी रखें"</string>
+ <string name="ok" msgid="1509280796718850364">"ठीक"</string>
+ <string name="page_info" msgid="4048529256302257195">"पृष्ठ जानकारी"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"पता:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"सुरक्षा चेतावनी"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"प्रमाणपत्र देखें"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"यह प्रमाणपत्र किसी विश्वस्त प्राधिकारी का नहीं है."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"साइट का नाम, प्रमाणपत्र के नाम से मिलान नहीं करता."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"इस प्रमाणपत्र की समय सीमा समाप्त हो गई है."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"यह प्रमाणपत्र अभी तक मान्य नहीं है."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"इस प्रमाणपत्र में एक अमान्य दिनांक है."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"यह प्रमाणपत्र अमान्य है."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"अज्ञात प्रमाणपत्र त्रुटि."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hr/strings.xml b/packages/CaptivePortalLogin/res/values-hr/strings.xml
index e44cd3b..11b1dd3 100644
--- a/packages/CaptivePortalLogin/res/values-hr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-hr/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj se pokušavate pridružiti ima sigurnosne poteškoće."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Na primjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi putem preglednika"</string>
+ <string name="ok" msgid="1509280796718850364">"U redu"</string>
+ <string name="page_info" msgid="4048529256302257195">"Informacije o stranici"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozorenje o sigurnosti"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Prikaži certifikat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ovaj certifikat ne potječe iz pouzdanog izvora."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Naziv web-lokacije ne podudara se s nazivom na certifikatu."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Ovaj je certifikat istekao."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Ovaj certifikat još nije važeći."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Ovaj certifikat ima nevažeći datum."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Ovaj certifikat nije valjan."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Nepoznata pogreška certifikata."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hu/strings.xml b/packages/CaptivePortalLogin/res/values-hu/strings.xml
index f15fb49..145e2ab 100644
--- a/packages/CaptivePortalLogin/res/values-hu/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-hu/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Biztonsági problémák vannak azzal a hálózattal, amelyhez csatlakozni szeretne."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Például lehet, hogy a bejelentkezési oldal nem a megjelenített szervezethez tartozik."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Folytatás ennek ellenére böngészőn keresztül"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Oldaladatok"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Cím:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Biztonsági figyelmeztetés"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tanúsítvány megtekintése"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ez a tanúsítvány nem hiteles tanúsítványkibocsátótól származik."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"A webhely neve nem egyezik a tanúsítványon lévő névvel."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"A tanúsítvány lejárt."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"A tanúsítvány még nem érvényes."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"A tanúsítvány dátuma érvénytelen."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Ez a tanúsítvány érvénytelen."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Ismeretlen tanúsítványhiba."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-in/strings.xml b/packages/CaptivePortalLogin/res/values-in/strings.xml
index 10e3de6..4a335dd 100644
--- a/packages/CaptivePortalLogin/res/values-in/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-in/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Jaringan yang ingin Anda masuki mengalami masalah keamanan."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Misalnya, halaman masuk mungkin bukan milik organisasi yang ditampilkan."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Tetap lanjutkan melalui browser"</string>
+ <string name="ok" msgid="1509280796718850364">"Oke"</string>
+ <string name="page_info" msgid="4048529256302257195">"Info laman"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Alamat:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Peringatan sertifikat"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Lihat sertifikat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikat ini tidak berasal dari otoritas tepercaya."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nama situs tidak cocok dengan nama pada sertifikat."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikat ini telah kedaluwarsa."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikat ini belum valid."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Tanggal sertifikat ini tidak valid."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Sertifikat ini tidak valid."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Kesalahan sertifikat tak dikenal."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-it/strings.xml b/packages/CaptivePortalLogin/res/values-it/strings.xml
index a01a553..2cc4038 100644
--- a/packages/CaptivePortalLogin/res/values-it/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-it/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"La rete a cui stai tentando di accedere presenta problemi di sicurezza."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Ad esempio, la pagina di accesso potrebbe non appartenere all\'organizzazione indicata."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continua comunque dal browser"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Info pagina"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Indirizzo:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avviso di sicurezza"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualizza certificato"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Questo certificato non proviene da un\'autorità attendibile."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Il nome del sito non corrisponde al nome nel certificato."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Il certificato è scaduto."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Questo certificato non è ancora valido."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Questo certificato presenta una data non valida."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Questo certificato non è valido."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Errore certificato sconosciuto."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-iw/strings.xml b/packages/CaptivePortalLogin/res/values-iw/strings.xml
index 8e7915d..527e692 100644
--- a/packages/CaptivePortalLogin/res/values-iw/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-iw/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"יש בעיות אבטחה ברשת שאליה אתה מנסה להתחבר."</string>
<string name="ssl_error_example" msgid="647898534624078900">"לדוגמה, ייתכן שדף ההתחברות אינו שייך לארגון המוצג."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"המשך בכל זאת באמצעות דפדפן"</string>
+ <string name="ok" msgid="1509280796718850364">"אישור"</string>
+ <string name="page_info" msgid="4048529256302257195">"פרטי דף"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"כתובת:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"אזהרת אבטחה"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"הצג אישור"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"אישור זה אינו מגיע מרשות אמינה."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"שם האתר לא תואם לשם באישור."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"פג תוקפו של אישור זה."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"אישור זה אינו חוקי עדיין."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"לאישור זה יש תאריך בלתי חוקי."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"אישור זה אינו חוקי."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"שגיאת אישור לא ידועה."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ja/strings.xml b/packages/CaptivePortalLogin/res/values-ja/strings.xml
index e275b95..bcc8686 100644
--- a/packages/CaptivePortalLogin/res/values-ja/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ja/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"接続しようとしているネットワークにセキュリティの問題があります。"</string>
<string name="ssl_error_example" msgid="647898534624078900">"たとえば、ログインページが表示されている組織に属していない可能性があります。"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ブラウザから続行"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"ページ情報"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"アドレス:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"セキュリティ警告"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"証明書を表示"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"この証明書は信頼できる認証機関のものではありません。"</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"サイト名と証明書上の名前が一致しません。"</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"この証明書は有効期限切れです。"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"この証明書はまだ有効ではありません。"</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"この証明書の日付は無効です。"</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"この証明書は無効です。"</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"不明な証明書エラーです。"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ko/strings.xml b/packages/CaptivePortalLogin/res/values-ko/strings.xml
index 75f2b48..7a7f7e0 100644
--- a/packages/CaptivePortalLogin/res/values-ko/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ko/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"가입하려는 네트워크에 보안 문제가 있습니다."</string>
<string name="ssl_error_example" msgid="647898534624078900">"예를 들어 로그인 페이지가 표시된 조직에 속하지 않을 수 있습니다."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"브라우저를 통해 계속하기"</string>
+ <string name="ok" msgid="1509280796718850364">"확인"</string>
+ <string name="page_info" msgid="4048529256302257195">"페이지 정보"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"주소:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"보안 경고"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"인증서 보기"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"신뢰할 수 있는 인증 기관에서 발급한 인증서가 아닙니다."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"사이트 이름이 인증서에 있는 것과 일치하지 않습니다."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"인증서가 만료되었습니다."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"인증서가 아직 유효하지 않습니다."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"인증서 날짜가 유효하지 않습니다."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"인증서가 잘못되었습니다."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"알 수 없는 인증서 오류입니다."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-lt/strings.xml b/packages/CaptivePortalLogin/res/values-lt/strings.xml
index 17da83f..158f7ce 100644
--- a/packages/CaptivePortalLogin/res/values-lt/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-lt/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Kilo tinklo, prie kurio bandote prisijungti, problemų."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Pavyzdžiui, prisijungimo puslapis gali nepriklausyti rodomai organizacijai."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Vis tiek tęsti naudojant naršyklę"</string>
+ <string name="ok" msgid="1509280796718850364">"Gerai"</string>
+ <string name="page_info" msgid="4048529256302257195">"Puslapio informacija"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adresas:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Saugos įspėjimas"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Žiūrėti sertifikatą"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Šį sertifikatą išdavė nepatikima įstaiga."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Svetainės pavadinimas neatitinka sertifikate nurodyto pavadinimo."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Šio sertifikato galiojimo laikas baigėsi."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Šis sertifikatas dar negalioja."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Šio sertifikato data netinkama."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Šis sertifikatas netinkamas."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Nežinoma sertifikato klaida."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-lv/strings.xml b/packages/CaptivePortalLogin/res/values-lv/strings.xml
index 95b8558..a42cb22 100644
--- a/packages/CaptivePortalLogin/res/values-lv/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-lv/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Tīklam, kuram mēģināt pievienoties, ir drošības problēmas."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Piemēram, pieteikšanās lapa, iespējams, nepieder norādītajai organizācijai."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Tik un tā turpināt, izmantojot pārlūkprogrammu"</string>
+ <string name="ok" msgid="1509280796718850364">"Labi"</string>
+ <string name="page_info" msgid="4048529256302257195">"Lapas informācija"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adrese:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Drošības brīdinājums"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Skatīt sertifikātu"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Šo sertifikātu nav izsniegusi uzticama iestāde."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Vietnes nosaukums neatbilst nosaukumam sertifikātā."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Šī sertifikāta derīguma termiņš ir beidzies."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Šis sertifikāts vēl nav derīgs."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Šī sertifikāta datums nav derīgs."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Šis sertifikāts nav derīgs."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Nezināma sertifikāta kļūda."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ms/strings.xml b/packages/CaptivePortalLogin/res/values-ms/strings.xml
index 933721a..aaa51c8 100644
--- a/packages/CaptivePortalLogin/res/values-ms/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ms/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Rangkaian yang anda cuba sertai mempunyai isu keselamatan."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Contohnya, halaman log masuk mungkin bukan milik organisasi yang ditunjukkan."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Teruskan juga melalui penyemak imbas"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Maklumat halaman"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Alamat:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Amaran keselamatan"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Lihat sijil"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sijil ini bukan daripada pihak berkuasa yang dipercayai."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nama tapak tidak sepadan dengan nama pada sijil."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Sijil ini telah tamat tempoh."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sijil ini belum lagi sah."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Sijil ini mempunyai tarikh yang tidak sah."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Sijil ini tidak sah."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Ralat sijil tidak diketahui."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-nb/strings.xml b/packages/CaptivePortalLogin/res/values-nb/strings.xml
index 0dd5b6c..29c23ed 100644
--- a/packages/CaptivePortalLogin/res/values-nb/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-nb/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Nettverket du prøver å logge på, har sikkerhetsproblemer."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Det er for eksempel mulig at påloggingssiden kanskje ikke tilhører organisasjonen som vises."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Fortsett likevel via nettleseren"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Sideinfo"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhetsadvarsel"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis sertifikat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikatet er ikke fra en pålitelig myndighet."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikatet er utløpt."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikatet er ikke gyldig ennå."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette sertifikatet har en ugyldig dato."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette sertifikatet er ugyldig."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukjent sertifikatfeil."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-nl/strings.xml b/packages/CaptivePortalLogin/res/values-nl/strings.xml
index 1c59601..2cbca06 100644
--- a/packages/CaptivePortalLogin/res/values-nl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-nl/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Het netwerk waarmee u verbinding probeert te maken, heeft beveiligingsproblemen."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Zo hoort de weergegeven inlogpagina misschien niet bij de weergegeven organisatie."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Toch doorgaan via browser"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Pagina-informatie"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Beveiligingsmelding"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Certificaat weergeven"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dit is geen certificaat van een vertrouwde autoriteit."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"De naam van deze site komt niet overeen met de naam op het certificaat."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Dit certificaat is verlopen."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dit certificaat is nog niet geldig."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dit certificaat heeft een ongeldige datum."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Dit certificaat is ongeldig."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Onbekende certificaatfout."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pl/strings.xml b/packages/CaptivePortalLogin/res/values-pl/strings.xml
index 17f20df..9ba066e 100644
--- a/packages/CaptivePortalLogin/res/values-pl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pl/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"W sieci, z którą próbujesz się połączyć, występują problemy z zabezpieczeniami."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Na przykład strona logowania może nie należeć do wyświetlanej organizacji."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Kontynuuj mimo to w przeglądarce"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Informacje o stronie"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Ostrzeżenie zabezpieczeń"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Wyświetl certyfikat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Certyfikat nie pochodzi od zaufanego urzędu."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nazwa witryny nie pasuje do nazwy na certyfikacie."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Ten certyfikat wygasł."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Certyfikat nie jest jeszcze ważny."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Certyfikat ma nieprawidłową datę."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Certyfikat jest nieprawidłowy."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Nieznany błąd certyfikatu"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml b/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
index 94b9d60..5bef235 100644
--- a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual está a tentar aceder tem problemas de segurança."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de início de sessão pode não pertencer à entidade apresentada."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim através do navegador"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Informações da página"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Endereço:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Aviso de segurança"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado não pertence a uma autoridade fidedigna."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"O nome do Web site não corresponde ao nome constante no certificado."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado expirou."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado ainda não é válido."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Este certificado tem uma data inválida."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado é inválido."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Erro: certificado desconhecido."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pt/strings.xml b/packages/CaptivePortalLogin/res/values-pt/strings.xml
index 3d1064c..ebe4148 100644
--- a/packages/CaptivePortalLogin/res/values-pt/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pt/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual você está tentando se conectar tem problemas de segurança."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim pelo navegador"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Informações da página"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Endereço:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Aviso de segurança"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualizar certificado"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado não é de uma autoridade confiável."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"O nome do site não corresponde ao nome no certificado."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado expirou."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado ainda não é válido."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Este certificado tem uma data inválida."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado é inválido."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Erro de certificado desconhecido."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ro/strings.xml b/packages/CaptivePortalLogin/res/values-ro/strings.xml
index cf1b6b5..e2e4eac 100644
--- a/packages/CaptivePortalLogin/res/values-ro/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ro/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Rețeaua la care încercați să vă conectați are probleme de securitate."</string>
<string name="ssl_error_example" msgid="647898534624078900">"De exemplu, este posibil ca pagina de conectare să nu aparțină organizației afișate."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuați oricum prin browser"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Informaţii pagină"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adresă:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avertisment de securitate"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vizualizaţi certificatul"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Acest certificat nu provine de la o autoritate de încredere."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Numele acestui site nu se potriveşte cu numele de pe certificat."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Acest certificat a expirat."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Acest certificat nu este încă valid."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Acest certificat are o dată nevalidă."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Acest certificat este nevalid."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Eroare de certificat necunoscută."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ru/strings.xml b/packages/CaptivePortalLogin/res/values-ru/strings.xml
index 6966bcd..c0153e6 100644
--- a/packages/CaptivePortalLogin/res/values-ru/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ru/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Сеть, к которой вы хотите подключиться, небезопасна."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Например, страница входа в аккаунт может быть фиктивной."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Игнорировать и открыть браузер"</string>
+ <string name="ok" msgid="1509280796718850364">"ОК"</string>
+ <string name="page_info" msgid="4048529256302257195">"Информация о странице"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Адрес:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Угроза безопасности"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Просмотреть сертификат"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Этот сертификат получен из ненадежных источников."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Название сайта не соответствует названию в сертификате."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Срок действия сертификата истек."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Сертификат еще не действителен."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Дата этого сертификата недействительна."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Этот сертификат недействителен."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Неизвестная ошибка сертификата."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sk/strings.xml b/packages/CaptivePortalLogin/res/values-sk/strings.xml
index 54763be..8ba24b1 100644
--- a/packages/CaptivePortalLogin/res/values-sk/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sk/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Sieť, ku ktorej sa pokúšate pripojiť, má problémy so zabezpečením"</string>
<string name="ssl_error_example" msgid="647898534624078900">"Napríklad prihlasovacia stránka nemusí patriť uvedenej organizácii."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Pokračovať pomocou prehliadača"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Informácie o stránke"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozornenie zabezpečenia"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zobraziť certifikát"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Tento certifikát nepochádza od dôveryhodnej autority."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Názov stránky sa nezhoduje s názvom uvedeným v certifikáte."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Platnosť certifikátu skončila."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Tento certifikát zatiaľ nie je platný."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Tento certifikát má neplatný dátum."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Tento certifikát je neplatný."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznáma chyba certifikátu."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sl/strings.xml b/packages/CaptivePortalLogin/res/values-sl/strings.xml
index 7dd0b37..b7d9a8a 100644
--- a/packages/CaptivePortalLogin/res/values-sl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sl/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Omrežje, ki se mu poskušate pridružiti, ima varnostne težave."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Stran za prijavo na primer morda ne pripada prikazani organizaciji."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Vseeno nadaljuj v brskalniku"</string>
+ <string name="ok" msgid="1509280796718850364">"V redu"</string>
+ <string name="page_info" msgid="4048529256302257195">"Podatki o strani"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Naslov:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Varnostno opozorilo"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Prikaži potrdilo"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Potrdila ni izdal zaupanja vreden overitelj."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Ime spletnega mesta se ne ujema z imenom na potrdilu."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Potrdilo je poteklo."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"To potrdilo še ni veljavno."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Potrdilo ima neveljaven datum."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"To potrdilo ni veljavno."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznana napaka potrdila."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sr/strings.xml b/packages/CaptivePortalLogin/res/values-sr/strings.xml
index f604289..967c8ba 100644
--- a/packages/CaptivePortalLogin/res/values-sr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sr/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Мрежа којој покушавате да се придружите има безбедносних проблема."</string>
<string name="ssl_error_example" msgid="647898534624078900">"На пример, страница за пријављивање можда не припада приказаној организацији."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Ипак настави преко прегледача"</string>
+ <string name="ok" msgid="1509280796718850364">"Потврди"</string>
+ <string name="page_info" msgid="4048529256302257195">"Информације о страници"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Адреса:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Безбедносно упозорење"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Прикажи сертификат"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Овај сертификат не потиче од поузданог ауторитета."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Назив сајта се не подудара са називом на сертификату."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Овај сертификат је истекао."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Овај сертификат још увек није важећи."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Датум овог сертификата је неважећи."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Овај сертификат је неважећи."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Непозната грешка сертификата."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sv/strings.xml b/packages/CaptivePortalLogin/res/values-sv/strings.xml
index 8cf7041..75356f0 100644
--- a/packages/CaptivePortalLogin/res/values-sv/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sv/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Nätverket du försöker ansluta till har säkerhetsproblem."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Det kan t.ex. hända att inloggningssidan inte tillhör den organisation som visas."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Fortsätt ändå via webbläsaren"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Sidinformation"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adress:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Säkerhetsvarning"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visa certifikat"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Certifikatet kommer inte från en betrodd utfärdare."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Webbplatsens namn stämmer inte med namnet på certifikatet."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Certifikatet har upphört att gälla."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Certifikatet är inte giltigt än."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Det här certifikatet har ett ogiltigt datum."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Certifikatet är ogiltigt."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Okänt certifikatfel."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sw/strings.xml b/packages/CaptivePortalLogin/res/values-sw/strings.xml
index 1c8b6e1..feb2dde 100644
--- a/packages/CaptivePortalLogin/res/values-sw/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sw/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Mtandao unaojaribu kujiunga nao una matatizo ya usalama."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Kwa mfano, ukurasa wa kuingia katika akaunti unaweza usiwe unamilikiwa na shirika lililoonyeshwa."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Endelea hata hivyo kupitia kivinjari"</string>
+ <string name="ok" msgid="1509280796718850364">"Sawa"</string>
+ <string name="page_info" msgid="4048529256302257195">"Maelezo ya ukurasa"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Anwani:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Ilani ya usalama"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tazama cheti"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Cheti hiki hakijatoka kwa mamlaka inayoaminika."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Jina la tovuti halilingani na jina lililo katika cheti."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Cheti hiki kimepitwa na muda"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Cheti bado si halali."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Cheti hiki kina tarehe batili."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Hati hii ni batili."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Hitilafu isiyojulikana ya cheti."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-th/strings.xml b/packages/CaptivePortalLogin/res/values-th/strings.xml
index 9a3a626..11a2131 100644
--- a/packages/CaptivePortalLogin/res/values-th/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-th/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"เครือข่ายที่คุณพยายามเข้าร่วมมีปัญหาด้านความปลอดภัย"</string>
<string name="ssl_error_example" msgid="647898534624078900">"ตัวอย่างเช่น หน้าเข้าสู่ระบบอาจไม่ใช่ขององค์กรที่แสดงไว้"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ดำเนินการต่อผ่านเบราว์เซอร์"</string>
+ <string name="ok" msgid="1509280796718850364">"ตกลง"</string>
+ <string name="page_info" msgid="4048529256302257195">"ข้อมูลหน้าเว็บ"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"ที่อยู่:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"คำเตือนเกี่ยวกับความปลอดภัย"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"ดูใบรับรอง"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"ใบรับรองนี้ไม่ได้มาจากผู้ออกที่เชื่อถือได้"</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"ชื่อไซต์ไม่ตรงกับในใบรับรอง"</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"ใบรับรองนี้หมดอายุแล้ว"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"ใบรับรองนี้ยังใช้งานไม่ได้"</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"ใบรับรองนี้มีวันที่ไม่ถูกต้อง"</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"ใบรับรองนี้ไม่ถูกต้อง"</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"ข้อผิดพลาดใบรับรองที่ไม่รู้จัก"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-tl/strings.xml b/packages/CaptivePortalLogin/res/values-tl/strings.xml
index 565ef8f..07a2479 100644
--- a/packages/CaptivePortalLogin/res/values-tl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-tl/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"May mga isyu sa seguridad ang network kung saan mo sinusubukang sumali."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Halimbawa, maaaring hindi sa organisasyong ipinapakita ang page sa pag-log in."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Magpatuloy pa rin sa pamamagitan ng browser"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Impormasyon ng pahina"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Address:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Babala sa seguridad"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tingnan ang certificate"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ang certificate ay hindi mula sa isang pinagkakatiwalaang kinauukulan."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Ang pangalan ng site ay hindi tumutugma sa pangalan sa certificate."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Nag-expire na ang certificate na ito."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Wala pang bisa ang certificate na ito."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Ang certificate ay mayroong di-wastong petsa."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Di-wasto ang certificate na ito."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Hindi kilalang error ng certificate."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-tr/strings.xml b/packages/CaptivePortalLogin/res/values-tr/strings.xml
index 73d2455..cdedd33 100644
--- a/packages/CaptivePortalLogin/res/values-tr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-tr/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Katılmaya çalıştığınız ağda güvenlik sorunları var."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Örneğin, giriş sayfası, gösterilen kuruluşa ait olmayabilir."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Yine de tarayıcıyla devam et"</string>
+ <string name="ok" msgid="1509280796718850364">"Tamam"</string>
+ <string name="page_info" msgid="4048529256302257195">"Sayfa bilgileri"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Güvenlik uyarısı"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Sertifikayı görüntüle"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Bu sertifika güvenilir bir yetkiliden değil."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Sitenin adı sertifika üzerindeki adla eşleşmiyor."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Bu sertifikanın süresi dolmuş."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Bu sertifika henüz geçerli değil."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Bu sertifikanın tarihi geçersiz."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Bu sertifika geçersiz."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Bilinmeyen sertifika hatası."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-uk/strings.xml b/packages/CaptivePortalLogin/res/values-uk/strings.xml
index 0e818d3..0f4cd16 100644
--- a/packages/CaptivePortalLogin/res/values-uk/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-uk/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"У мережі, до якої ви намагаєтеся під’єднатись, є проблеми з безпекою."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Наприклад, сторінка входу може не належати вказаній організації."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Усе одно продовжити у веб-переглядачі"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Інфо про стор."</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Адреса:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Застереж. про небезп."</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Переглянути сертиф."</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Сертифікат видано ненадійним центром сертифікації."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Назва сайту не збігається з назвою в сертифікаті."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Термін дії сертиф. завершився."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Цей сертифікат ще не дійсний."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Цей сертифікат має недійсну дату."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Цей сертифікат недійсний."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Помилка невідомого сертифіката."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-vi/strings.xml b/packages/CaptivePortalLogin/res/values-vi/strings.xml
index e51d2aa..9c702b9 100644
--- a/packages/CaptivePortalLogin/res/values-vi/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-vi/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Mạng mà bạn đang cố gắng tham gia có vấn đề về bảo mật."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Ví dụ, trang đăng nhập có thể không thuộc về tổ chức được hiển thị."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Vẫn tiếp tục qua trình duyệt"</string>
+ <string name="ok" msgid="1509280796718850364">"OK"</string>
+ <string name="page_info" msgid="4048529256302257195">"Thông tin trang"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Địa chỉ:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Cảnh báo bảo mật"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Xem chứng chỉ"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Chứng chỉ này không xuất phát từ tổ chức phát hành đáng tin cậy."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Tên của trang web không khớp với tên trên chứng chỉ."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Chứng chỉ này đã hết hạn."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Chứng chỉ này chưa hợp lệ."</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Chứng chỉ này có ngày không hợp lệ."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Chứng chỉ này không hợp lệ."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Lỗi chứng chỉ không xác định."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
index ce822e7..70c2a08 100644
--- a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"您尝试加入的网络存在安全问题。"</string>
<string name="ssl_error_example" msgid="647898534624078900">"例如,登录页面可能并不属于页面上显示的单位。"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"仍然通过浏览器继续操作"</string>
+ <string name="ok" msgid="1509280796718850364">"确定"</string>
+ <string name="page_info" msgid="4048529256302257195">"网页信息"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"网址:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全警告"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"查看证书"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"该证书并非来自可信的授权中心。"</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"网站的名称与证书上的名称不一致。"</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"该证书已过期。"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"该证书尚未生效。"</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"该证书的日期无效。"</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"该证书无效。"</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"未知证书错误。"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
index 9010e1e..df1c700 100644
--- a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"您正在嘗試加入的網絡有安全性問題。"</string>
<string name="ssl_error_example" msgid="647898534624078900">"例如,登入頁面並不屬於所顯示的機構。"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"透過瀏覽器繼續"</string>
+ <string name="ok" msgid="1509280796718850364">"確定"</string>
+ <string name="page_info" msgid="4048529256302257195">"網頁資訊"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"地址:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全性警告"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"查看憑證"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"這個憑證並非由受信任的權威機構發出。"</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"網站名稱與憑證上的名稱不相符。"</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"這個憑證已過期。"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"這個憑證尚未生效。"</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"此憑證的日期無效。"</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"此憑證是無效的。"</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"不明的憑證錯誤。"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
index 5b535e2..2a2e397 100644
--- a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"你嘗試加入的網路有安全問題。"</string>
<string name="ssl_error_example" msgid="647898534624078900">"例如,登入網頁中顯示的機構可能並非該網頁實際隸屬的機構。"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"透過瀏覽器繼續"</string>
+ <string name="ok" msgid="1509280796718850364">"確定"</string>
+ <string name="page_info" msgid="4048529256302257195">"頁面資訊"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"位址:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全性警告"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"檢視憑證"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"這個憑證並非來自信任的授權單位。"</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"網站名稱與憑證上的名稱不相符。"</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"此憑證已過期"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"這個憑證尚未生效。"</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"這個憑證的日期無效。"</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"這個憑證無效。"</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"不明的憑證錯誤。"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zu/strings.xml b/packages/CaptivePortalLogin/res/values-zu/strings.xml
index 866ba18..7943645 100644
--- a/packages/CaptivePortalLogin/res/values-zu/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zu/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Inethiwekhi ozama ukuyijoyina inezinkinga zokuvikela."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Isibonelo, ikhasi lokungena ngemvume kungenzeka lingelenhlangano ebonisiwe."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Qhubeka noma kunjalo ngesiphequluli"</string>
+ <string name="ok" msgid="1509280796718850364">"KULUNGILE"</string>
+ <string name="page_info" msgid="4048529256302257195">"Ulwazi lekhasi"</string>
+ <string name="page_info_address" msgid="2222306609532903254">"Ikheli:"</string>
+ <string name="ssl_security_warning_title" msgid="6607795404322797541">"Isexwayiso sokuvikeleka"</string>
+ <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Buka isitifiketi"</string>
+ <string name="ssl_error_untrusted" msgid="7754507359360636447">"Lesi sitifiketi asiphumi embusweni othembekile."</string>
+ <string name="ssl_error_mismatch" msgid="3809794439740523641">"Igama lale ngosi alifani negama elikusitifiketi."</string>
+ <string name="ssl_error_expired" msgid="5739349389499575559">"Lesi sitifiketi siphelelwe yisikhathi"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Lesi sitifiketi asilungile okwamanje"</string>
+ <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Lesi sitifiketi sinosuku olungalungile."</string>
+ <string name="ssl_error_invalid" msgid="9041704741505449967">"Lesi sitifiketi asilungile."</string>
+ <string name="ssl_error_unknown" msgid="5679243486524754571">"Iphutha lesitifiketi elingaziwa."</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values/strings.xml b/packages/CaptivePortalLogin/res/values/strings.xml
index f486fe4..e9698db 100644
--- a/packages/CaptivePortalLogin/res/values/strings.xml
+++ b/packages/CaptivePortalLogin/res/values/strings.xml
@@ -9,5 +9,17 @@
<string name="ssl_error_warning">The network you’re trying to join has security issues.</string>
<string name="ssl_error_example">For example, the login page may not belong to the organization shown.</string>
<string name="ssl_error_continue">Continue anyway via browser</string>
+ <string name="ssl_error_untrusted">This certificate isn\'t from a trusted authority.</string>
+ <string name="ssl_error_mismatch">The name of the site doesn\'t match the name on the certificate.</string>
+ <string name="ssl_error_expired">This certificate has expired.</string>
+ <string name="ssl_error_not_yet_valid">This certificate isn\'t valid yet.</string>
+ <string name="ssl_error_date_invalid">This certificate has an invalid date.</string>
+ <string name="ssl_error_invalid">This certificate is invalid.</string>
+ <string name="ssl_error_unknown">Unknown certificate error.</string>
+ <string name="ssl_security_warning_title">Security warning</string>
+ <string name="ssl_error_view_certificate">View certificate</string>
+ <string name="ok">OK</string>
+ <string name="page_info_address">Address:</string>
+ <string name="page_info">Page info</string>
</resources>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 0ba37ae..83084c5 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -20,8 +20,10 @@
import static android.net.captiveportal.CaptivePortalProbeSpec.HTTP_LOCATION_HEADER_NAME;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.LoadedApk;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.CaptivePortal;
@@ -33,6 +35,7 @@
import android.net.Proxy;
import android.net.Uri;
import android.net.captiveportal.CaptivePortalProbeSpec;
+import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.net.wifi.WifiInfo;
import android.os.Build;
@@ -42,8 +45,9 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.TypedValue;
import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -52,8 +56,8 @@
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
-import android.webkit.WebView;
import android.webkit.WebViewClient;
+import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -276,6 +280,13 @@
@Override
public void onDestroy() {
super.onDestroy();
+ final WebView webview = (WebView) findViewById(R.id.webview);
+ if (webview != null) {
+ webview.stopLoading();
+ webview.setWebViewClient(null);
+ webview.setWebChromeClient(null);
+ webview.destroy();
+ }
if (mNetworkCallback != null) {
// mNetworkCallback is not null if mUrl is not null.
mCm.unregisterNetworkCallback(mNetworkCallback);
@@ -382,6 +393,7 @@
private static final String INTERNAL_ASSETS = "file:///android_asset/";
private final String mBrowserBailOutToken = Long.toString(new Random().nextLong());
+ private final String mCertificateOutToken = Long.toString(new Random().nextLong());
// How many Android device-independent-pixels per scaled-pixel
// dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp)
private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
@@ -397,6 +409,10 @@
return mPagesLoaded > 1;
}
+ private String mSslErrorTitle = null;
+ private SslErrorHandler mSslErrorHandler = null;
+ private SslError mSslError = null;
+
@Override
public void onPageStarted(WebView view, String urlString, Bitmap favicon) {
if (urlString.contains(mBrowserBailOutToken)) {
@@ -473,12 +489,16 @@
logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR);
final String sslErrorPage = makeSslErrorPage();
view.loadDataWithBaseURL(INTERNAL_ASSETS, sslErrorPage, "text/HTML", "UTF-8", null);
+ mSslErrorTitle = view.getTitle() == null ? "" : view.getTitle();
+ mSslErrorHandler = handler;
+ mSslError = error;
}
private String makeSslErrorPage() {
final String warningMsg = getString(R.string.ssl_error_warning);
final String exampleMsg = getString(R.string.ssl_error_example);
final String continueMsg = getString(R.string.ssl_error_continue);
+ final String certificateMsg = getString(R.string.ssl_error_view_certificate);
return String.join("\n",
"<html>",
"<head>",
@@ -516,13 +536,18 @@
" text-decoration:none;",
" text-transform:uppercase;",
" }",
+ " a.certificate {",
+ " margin-top:0px;",
+ " }",
" </style>",
"</head>",
"<body>",
" <p><img src=quantum_ic_warning_amber_96.png><br>",
" <div class=warn>" + warningMsg + "</div>",
" <div class=example>" + exampleMsg + "</div>",
- " <a href=" + mBrowserBailOutToken + ">" + continueMsg + "</a>",
+ " <a href=" + mBrowserBailOutToken + ">" + continueMsg + "</a><br>",
+ " <a class=certificate href=" + mCertificateOutToken + ">" + certificateMsg +
+ "</a>",
"</body>",
"</html>");
}
@@ -533,8 +558,50 @@
startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
return true;
}
+ if (url.contains(mCertificateOutToken) && mSslError != null) {
+ showSslAlertDialog(mSslErrorHandler, mSslError, mSslErrorTitle);
+ return true;
+ }
return false;
}
+ private void showSslAlertDialog(SslErrorHandler handler, SslError error, String title) {
+ final LayoutInflater factory = LayoutInflater.from(CaptivePortalLoginActivity.this);
+ final View sslWarningView = factory.inflate(R.layout.ssl_warning, null);
+
+ // Set Security certificate
+ setViewSecurityCertificate(sslWarningView.findViewById(R.id.certificate_layout), error);
+ ((TextView) sslWarningView.findViewById(R.id.ssl_error_type))
+ .setText(sslErrorName(error));
+ ((TextView) sslWarningView.findViewById(R.id.title)).setText(mSslErrorTitle);
+ ((TextView) sslWarningView.findViewById(R.id.address)).setText(error.getUrl());
+
+ AlertDialog sslAlertDialog = new AlertDialog.Builder(CaptivePortalLoginActivity.this)
+ .setTitle(R.string.ssl_security_warning_title)
+ .setView(sslWarningView)
+ .setPositiveButton(R.string.ok, (DialogInterface dialog, int whichButton) -> {
+ // handler.cancel is called via OnCancelListener.
+ dialog.cancel();
+ })
+ .setOnCancelListener((DialogInterface dialogInterface) -> handler.cancel())
+ .create();
+ sslAlertDialog.show();
+ }
+
+ private void setViewSecurityCertificate(LinearLayout certificateLayout, SslError error) {
+ SslCertificate cert = error.getCertificate();
+
+ View certificateView = cert.inflateCertificateView(CaptivePortalLoginActivity.this);
+ final LinearLayout placeholder = (LinearLayout) certificateView
+ .findViewById(com.android.internal.R.id.placeholder);
+ LayoutInflater factory = LayoutInflater.from(CaptivePortalLoginActivity.this);
+
+ TextView textView = (TextView) factory.inflate(
+ R.layout.ssl_error_msg, placeholder, false);
+ textView.setText(sslErrorMessage(error));
+ placeholder.addView(textView);
+
+ certificateLayout.addView(certificateView);
+ }
}
private class MyWebChromeClient extends WebChromeClient {
@@ -587,4 +654,18 @@
private static String sslErrorName(SslError error) {
return SSL_ERRORS.get(error.getPrimaryError(), "UNKNOWN");
}
+
+ private static final SparseArray<Integer> SSL_ERROR_MSGS = new SparseArray<>();
+ static {
+ SSL_ERROR_MSGS.put(SslError.SSL_NOTYETVALID, R.string.ssl_error_not_yet_valid);
+ SSL_ERROR_MSGS.put(SslError.SSL_EXPIRED, R.string.ssl_error_expired);
+ SSL_ERROR_MSGS.put(SslError.SSL_IDMISMATCH, R.string.ssl_error_mismatch);
+ SSL_ERROR_MSGS.put(SslError.SSL_UNTRUSTED, R.string.ssl_error_untrusted);
+ SSL_ERROR_MSGS.put(SslError.SSL_DATE_INVALID, R.string.ssl_error_date_invalid);
+ SSL_ERROR_MSGS.put(SslError.SSL_INVALID, R.string.ssl_error_invalid);
+ }
+
+ private static Integer sslErrorMessage(SslError error) {
+ return SSL_ERROR_MSGS.get(error.getPrimaryError(), R.string.ssl_error_unknown);
+ }
}
diff --git a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
index 8447b08..8af20e2 100644
--- a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
@@ -17,7 +17,7 @@
<resources>
<style name="SettingsSpinnerTitleBar">
- <item name="android:textAppearance">?android:attr/textAppearance</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
<item name="android:paddingStart">16dp</item>
<item name="android:paddingEnd">36dp</item>
<item name="android:paddingTop">8dp</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 0dbc037..2f082b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -152,7 +152,11 @@
private boolean mNetworkScoringUiEnabled;
private long mMaxSpeedLabelScoreCacheAge;
-
+ private static final String WIFI_SECURITY_PSK = "PSK";
+ private static final String WIFI_SECURITY_EAP = "EAP";
+ private static final String WIFI_SECURITY_SAE = "SAE";
+ private static final String WIFI_SECURITY_OWE = "OWE";
+ private static final String WIFI_SECURITY_SUITE_B_192 = "SUITE_B_192";
@VisibleForTesting
Scanner mScanner;
@@ -505,13 +509,18 @@
* {@link #updateAccessPoints(List, List)}.
*/
private void fetchScansAndConfigsAndUpdateAccessPoints() {
- final List<ScanResult> newScanResults = mWifiManager.getScanResults();
+ List<ScanResult> newScanResults = mWifiManager.getScanResults();
+
+ // Filter all unsupported networks from the scan result list
+ final List<ScanResult> filteredScanResults =
+ filterScanResultsByCapabilities(newScanResults);
+
if (isVerboseLoggingEnabled()) {
- Log.i(TAG, "Fetched scan results: " + newScanResults);
+ Log.i(TAG, "Fetched scan results: " + filteredScanResults);
}
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
- updateAccessPoints(newScanResults, configs);
+ updateAccessPoints(filteredScanResults, configs);
}
/** Update the internal list of access points. */
@@ -937,4 +946,49 @@
mListener.onAccessPointsChanged();
}
+
+ /**
+ * Filters unsupported networks from scan results. New WPA3 networks and OWE networks
+ * may not be compatible with the device HW/SW.
+ * @param scanResults List of scan results
+ * @return List of filtered scan results based on local device capabilities
+ */
+ private List<ScanResult> filterScanResultsByCapabilities(List<ScanResult> scanResults) {
+ if (scanResults == null) {
+ return null;
+ }
+
+ // Get and cache advanced capabilities
+ final boolean isOweSupported = mWifiManager.isOweSupported();
+ final boolean isSaeSupported = mWifiManager.isWpa3SaeSupported();
+ final boolean isSuiteBSupported = mWifiManager.isWpa3SuiteBSupported();
+
+ List<ScanResult> filteredScanResultList = new ArrayList<>();
+
+ // Iterate through the list of scan results and filter out APs which are not
+ // compatible with our device.
+ for (ScanResult scanResult : scanResults) {
+ if (scanResult.capabilities.contains(WIFI_SECURITY_PSK)) {
+ // All devices (today) support RSN-PSK or WPA-PSK
+ // Add this here because some APs may support both PSK and SAE and the check
+ // below will filter it out.
+ filteredScanResultList.add(scanResult);
+ continue;
+ }
+
+ if ((scanResult.capabilities.contains(WIFI_SECURITY_SUITE_B_192) && !isSuiteBSupported)
+ || (scanResult.capabilities.contains(WIFI_SECURITY_SAE) && !isSaeSupported)
+ || (scanResult.capabilities.contains(WIFI_SECURITY_OWE) && !isOweSupported)) {
+ if (isVerboseLoggingEnabled()) {
+ Log.v(TAG, "filterScanResultsByCapabilities: Filtering SSID "
+ + scanResult.SSID + " with capabilities: " + scanResult.capabilities);
+ }
+ } else {
+ // Safe to add
+ filteredScanResultList.add(scanResult);
+ }
+ }
+
+ return filteredScanResultList;
+ }
}
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index 8247c27..36d0659 100644
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -16,7 +16,7 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="#bbbbbb" />
+ <solid android:color="#4a4a4a" />
<padding android:padding="@dimen/ongoing_appops_chip_bg_padding" />
<corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_camera.xml b/packages/SystemUI/res/drawable/stat_sys_camera.xml
new file mode 100644
index 0000000..eb3e963
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_camera.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="3dp"
+ android:insetRight="3dp">
+ <vector
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFF"
+ android:pathData="M20,5h-3.17L15,3H9L7.17,5H4C2.9,5 2,5.9 2,7v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7C22,5.9 21.1,5 20,5zM20,19H4V7h16V19zM12,9c-2.21,0 -4,1.79 -4,4c0,2.21 1.79,4 4,4s4,-1.79 4,-4C16,10.79 14.21,9 12,9z"/>
+ </vector>
+</inset>
diff --git a/packages/SystemUI/res/drawable/stat_sys_mic_none.xml b/packages/SystemUI/res/drawable/stat_sys_mic_none.xml
new file mode 100644
index 0000000..d6bdf9f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_mic_none.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="18dp"
+ android:height="18dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFF"
+ android:pathData="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14zM11,5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v6c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V5z"/>
+ <path
+ android:fillColor="#FFF"
+ android:pathData="M17,11c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c3.39,-0.49 6,-3.39 6,-6.92H17z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index ddefb6a..cbdd51b 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -18,11 +18,14 @@
<com.android.systemui.privacy.OngoingPrivacyChip
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/privacy_chip"
- android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_margin="@dimen/ongoing_appops_chip_margin"
+ android:layout_width="wrap_content"
+ android:layout_marginLeft="@dimen/ongoing_appops_chip_margin"
+ android:layout_marginRight="@dimen/ongoing_appops_chip_margin"
+ android:layout_marginTop="@dimen/ongoing_appops_top_chip_margin"
+ android:layout_marginBottom="@dimen/ongoing_appops_top_chip_margin"
android:gravity="center_vertical|center_horizontal"
- android:layout_gravity="center_vertical|end"
+ android:layout_gravity="center_vertical|start"
android:orientation="horizontal"
android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
@@ -38,12 +41,17 @@
/>
<TextView
- android:id="@+id/app_name"
+ android:id="@+id/text_container"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:singleLine="true"
android:ellipsize="end"
+ android:lines="1"
android:layout_gravity="center_vertical|end"
android:gravity="center_vertical"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:textColor="@color/status_bar_clock_color"
+ android:layout_marginStart="@dimen/ongoing_appops_chip_icon_margin"
+ android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin"
/>
</com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
index b5e24a0..2f7d486 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
@@ -29,22 +29,30 @@
android:orientation="vertical"
android:padding="@dimen/ongoing_appops_dialog_content_padding">
- <LinearLayout
- android:id="@+id/icons_container"
+ <TextView
+ android:id="@+id/title"
android:layout_width="match_parent"
- android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
- android:orientation="horizontal"
+ android:layout_height="wrap_content"
android:gravity="center"
- android:importantForAccessibility="no"
+ android:textDirection="locale"
+ android:textAppearance="@style/TextAppearance.AppOpsDialog.Title"
+ android:textColor="@*android:color/text_color_primary"
+ android:paddingStart="@dimen/ongoing_appops_dialog_title_padding"
+ android:paddingEnd="@dimen/ongoing_appops_dialog_title_padding"
+ android:paddingBottom="@dimen/ongoing_appops_dialog_sep"
/>
<LinearLayout
- android:id="@+id/text_container"
+ android:id="@+id/items_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="start"
/>
+
+ <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
+ android:visibility="gone" />
+
</LinearLayout>
</ScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
new file mode 100644
index 0000000..f05f7ba
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fillViewport="true"
+ android:orientation="horizontal"
+ android:layout_marginTop="@dimen/ongoing_appops_dialog_text_margin"
+ android:focusable="true" >
+
+ <ImageView
+ android:id="@+id/app_icon"
+ android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+ android:layout_width="@dimen/ongoing_appops_dialog_icon_height"
+ />
+
+ <TextView
+ android:id="@+id/app_name"
+ android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:gravity="bottom|start"
+ android:textDirection="locale"
+ android:textAppearance="@style/TextAppearance.AppOpsDialog.Item"
+ android:textColor="@*android:color/text_color_primary"
+ android:paddingStart="@dimen/ongoing_appops_dialog_text_padding"
+ android:paddingEnd="@dimen/ongoing_appops_dialog_text_padding"
+
+ />
+
+ <LinearLayout
+ android:id="@+id/icons"
+ android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+ android:layout_width="wrap_content"
+ android:gravity="end"
+ android:visibility="gone"
+ />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index f554150..890bf5d 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -62,7 +62,7 @@
android:layout_weight="1"
android:layout_marginEnd="32dp"
android:ellipsize="marquee"
- android:textAppearance="@style/TextAppearance.QS.TileLabel"
+ android:textAppearance="@style/TextAppearance.QS.CarrierInfo"
android:textColor="?android:attr/textColorPrimary"
android:textDirection="locale"
android:singleLine="true" />
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 980442c..f34161e 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -79,7 +79,7 @@
android:padding="0dp"
android:visibility="gone"
android:gravity="center"
- android:textAppearance="@style/TextAppearance.QS.TileLabel"
+ android:textAppearance="@style/TextAppearance.QS.TileLabel.Secondary"
android:textColor="?android:attr/textColorSecondary"/>
<View
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index e7f2c51..22b8d2f 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -59,7 +59,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
- android:gravity="center_vertical|end">
+ android:gravity="center_vertical|end" >
<include layout="@layout/ongoing_privacy_chip" />
@@ -67,6 +67,7 @@
android:id="@+id/battery"
android:layout_height="match_parent"
android:layout_width="wrap_content"
- android:gravity="center_vertical|end" />
+ android:gravity="center_vertical|end"
+ android:layout_gravity="center_vertical|end" />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index bb0c6f6..df858f0 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -34,4 +34,5 @@
<bool name="quick_settings_wide">true</bool>
<dimen name="qs_detail_margin_top">0dp</dimen>
<dimen name="qs_paged_tile_layout_padding_bottom">0dp</dimen>
+ <dimen name="ongoing_appops_top_chip_margin">2dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0e41a7f..97f5f86 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -369,6 +369,7 @@
<dimen name="qs_page_indicator_height">8dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
+ <dimen name="qs_carrier_info_text_size">14sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
<dimen name="qs_panel_padding">16dp</dimen>
<dimen name="qs_dual_tile_height">112dp</dimen>
@@ -939,18 +940,34 @@
that just start below the notch. -->
<dimen name="display_cutout_touchable_region_size">12dp</dimen>
+ <!-- Padding below Ongoing App Ops dialog title -->
+ <dimen name="ongoing_appops_dialog_sep">16dp</dimen>
+ <!--Padding around text items in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_text_padding">16dp</dimen>
<!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
- <dimen name="ongoing_appops_dialog_icon_height">48dp</dimen>
+ <dimen name="ongoing_appops_dialog_icon_height">28dp</dimen>
<!-- Margin between text lines in Ongoing App Ops dialog -->
<dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
+ <!-- Side padding of title in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_title_padding">10dp</dimen>
<!-- Padding around Ongoing App Ops dialog content -->
<dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
- <!-- Margins around the Ongoing App Ops chip. In landscape, the side margins are 0 -->
+ <!-- Side margins around the Ongoing App Ops chip-->
<dimen name="ongoing_appops_chip_margin">12dp</dimen>
+ <!-- Top and bottom margins around the Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_top_chip_margin">12dp</dimen>
<!-- Start and End padding for Ongoing App Ops chip -->
<dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
<!-- Padding between background of Ongoing App Ops chip and content -->
- <dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
+ <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen>
+ <!-- Margin between icons of Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_icon_margin">4dp</dimen>
+ <!-- Icon size of Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_icon_size">18dp</dimen>
<!-- Radius of Ongoing App Ops chip corners -->
<dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
+ <!-- Text size for Ongoing App Ops dialog title -->
+ <dimen name="ongoing_appops_dialog_title_size">24sp</dimen>
+ <!-- Text size for Ongoing App Ops dialog items -->
+ <dimen name="ongoing_appops_dialog_item_size">20sp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 50454fc..4a0bc9b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2250,39 +2250,48 @@
app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
<string name="heap_dump_tile_name">Dump SysUI Heap</string>
+ <!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
+ <string name="ongoing_privacy_chip_multiple_apps"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</string>
+
<!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
<string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
<!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
<string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
- <!-- Action on Ongoing Privacy Dialog to open application [CHAR LIMIT=10]-->
- <string name="ongoing_privacy_dialog_open_app">Open app</string>
+ <!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]-->
+ <string name="ongoing_privacy_chip_content_multiple_apps_single_op"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</string>
<!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
<string name="ongoing_privacy_dialog_cancel">Cancel</string>
- <!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
- <string name="ongoing_privacy_dialog_okay">Okay</string>
+ <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=15]-->
+ <string name="ongoing_privacy_dialog_open_settings">View details</string>
- <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=10]-->
- <string name="ongoing_privacy_dialog_open_settings">Settings</string>
+ <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
+ <string name="ongoing_privacy_dialog_single_app_title">App using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
- <!-- Text for item in Ongoing Privacy Dialog when only one app is using a particular type of app op [CHAR LIMIT=NONE] -->
- <string name="ongoing_privacy_dialog_app_item"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="type" example="camera">%2$s</xliff:g> for the last <xliff:g id="time" example="3">%3$d</xliff:g> min</string>
+ <!-- Text for item in Ongoing Privacy Dialog title when multiple apps is using app ops [CHAR LIMIT=NONE] -->
+ <string name="ongoing_privacy_dialog_multiple_apps_title">Apps using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
- <!-- Text for item in Ongoing Privacy Dialog when only multiple apps are using a particular type of app op [CHAR LIMIT=NONE] -->
- <string name="ongoing_privacy_dialog_apps_item"><xliff:g id="apps" example="Camera, Phone">%1$s</xliff:g> are using your <xliff:g id="type" example="camera">%2$s</xliff:g></string>
+ <!-- Separator for types. Include spaces before and after if needed [CHAR LIMIT=10] -->
+ <string name="ongoing_privacy_dialog_separator">,\u0020</string>
- <!-- Text for Ongoing Privacy Dialog when a single app is using app ops [CHAR LIMIT=NONE] -->
- <string name="ongoing_privacy_dialog_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g></string>
+ <!-- Separator for types, before last type. Include spaces before and after if needed [CHAR LIMIT=10] -->
+ <string name="ongoing_privacy_dialog_last_separator">\u0020and\u0020</string>
- <!-- Text for camera app op [CHAR LIMIT=12]-->
+ <!-- Text for camera app op [CHAR LIMIT=20]-->
<string name="privacy_type_camera">camera</string>
- <!-- Text for location app op [CHAR LIMIT=12]-->
+ <!-- Text for location app op [CHAR LIMIT=20]-->
<string name="privacy_type_location">location</string>
- <!-- Text for microphone app op [CHAR LIMIT=12]-->
+ <!-- Text for microphone app op [CHAR LIMIT=20]-->
<string name="privacy_type_microphone">microphone</string>
+
+ <!-- Text for indicating extra apps using app ops [CHAR LIMIT=NONE] -->
+ <plurals name="ongoing_privacy_dialog_overflow_text">
+ <item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> other app</item>
+ <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6244e1c..e9aa1b6 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -125,7 +125,7 @@
<style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
<item name="android:textSize">@dimen/status_bar_clock_size</item>
- <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:textColor">@color/status_bar_clock_color</item>
</style>
@@ -135,7 +135,7 @@
<style name="TextAppearance.StatusBar.Expanded.Clock">
<item name="android:textSize">@dimen/qs_time_expanded_size</item>
- <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textStyle">normal</item>
</style>
@@ -240,9 +240,31 @@
<style name="TextAppearance.QS.TileLabel">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ </style>
+
+ <style name="TextAppearance.QS.TileLabel.Secondary">
+ <item name="android:textSize">@dimen/qs_tile_text_size</item>
<item name="android:fontFamily">sans-serif</item>
</style>
+ <style name="TextAppearance.QS.CarrierInfo">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize">@dimen/qs_carrier_info_text_size</item>
+ </style>
+
+ <style name="TextAppearance.AppOpsDialog" />
+
+ <style name="TextAppearance.AppOpsDialog.Title">
+ <item name="android:textSize">@dimen/ongoing_appops_dialog_title_size</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ </style>
+
+ <style name="TextAppearance.AppOpsDialog.Item">
+ <item name="android:textSize">@dimen/ongoing_appops_dialog_item_size</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ </style>
+
<style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index e3584cf..3666400 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -88,8 +88,6 @@
private Runnable mWatchLongPress;
private final long mLongPressTimeout;
- protected boolean mSwipingInProgress;
-
final private int[] mTmpPos = new int[2];
private final int mFalsingThreshold;
private boolean mTouchAboveFalsingThreshold;
@@ -130,10 +128,6 @@
mDisableHwLayers = disableHwLayers;
}
- public boolean isSwipingInProgress() {
- return mSwipingInProgress;
- }
-
private float getPos(MotionEvent ev) {
return mSwipeDirection == X ? ev.getX() : ev.getY();
}
@@ -325,7 +319,6 @@
if (Math.abs(delta) > mPagingTouchSlop
&& Math.abs(delta) > Math.abs(deltaPerpendicular)) {
if (mCallback.canChildBeDragged(mCurrView)) {
- mSwipingInProgress = true;
mCallback.onBeginDrag(mCurrView);
mDragging = true;
mInitialTouchPos = getPos(ev);
@@ -445,7 +438,6 @@
wasRemoved = row.isRemoved();
}
if (!mCancelled || wasRemoved) {
- mSwipingInProgress = false;
mCallback.onChildDismissed(animView);
}
if (endAction != null) {
@@ -637,7 +629,6 @@
!swipedFastEnough() /* useAccelerateInterpolator */);
} else {
// snappity
- mSwipingInProgress = false;
mCallback.onDragCancelled(mCurrView);
snapChild(mCurrView, 0 /* leftTarget */, velocity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index fc1baef..d3715d0 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -15,7 +15,6 @@
package com.android.systemui.privacy
import android.content.Context
-import android.graphics.Color
import android.util.AttributeSet
import android.view.ViewGroup
import android.widget.ImageView
@@ -30,7 +29,13 @@
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
- private lateinit var appName: TextView
+ private val iconMargin =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin)
+ private val iconSize =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
+ val iconColor = context.resources.getColor(
+ R.color.status_bar_clock_color, context.theme)
+ private lateinit var text: TextView
private lateinit var iconsContainer: LinearLayout
var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>())
var privacyList = emptyList<PrivacyItem>()
@@ -43,7 +48,7 @@
override fun onFinishInflate() {
super.onFinishInflate()
- appName = findViewById(R.id.app_name)
+ text = findViewById(R.id.text_container)
iconsContainer = findViewById(R.id.icons_container)
}
@@ -53,39 +58,52 @@
iconsContainer.removeAllViews()
dialogBuilder.generateIcons().forEach {
it.mutate()
- it.setTint(Color.WHITE)
- iconsContainer.addView(ImageView(context).apply {
+ it.setTint(iconColor)
+ val image = ImageView(context).apply {
setImageDrawable(it)
- maxHeight = this@OngoingPrivacyChip.height
- })
+ scaleType = ImageView.ScaleType.CENTER_INSIDE
+ }
+ iconsContainer.addView(image, iconSize, iconSize)
+ val lp = image.layoutParams as MarginLayoutParams
+ lp.marginStart = iconMargin
+ image.layoutParams = lp
}
}
- if (privacyList.isEmpty()) {
- return
- } else {
+ if (!privacyList.isEmpty()) {
generateContentDescription()
setIcons(builder, iconsContainer)
- appName.visibility = GONE
- builder.app?.let {
- appName.apply {
- setText(it.applicationName)
- setTextColor(Color.WHITE)
- visibility = VISIBLE
+ text.visibility = if (builder.types.size == 1) VISIBLE else GONE
+ if (builder.types.size == 1) {
+ if (builder.app != null) {
+ text.setText(builder.app?.applicationName)
+ } else {
+ text.text = context.getString(R.string.ongoing_privacy_chip_multiple_apps,
+ builder.appsAndTypes.size)
}
}
+ } else {
+ text.visibility = GONE
+ iconsContainer.removeAllViews()
}
requestLayout()
}
private fun generateContentDescription() {
- val typesText = builder.generateTypesText()
- if (builder.app != null) {
- contentDescription = context.getString(R.string.ongoing_privacy_chip_content_single_app,
- builder.app?.applicationName, typesText)
- } else {
+ val typesText = builder.joinTypes()
+ if (builder.types.size > 1) {
contentDescription = context.getString(
R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
+ } else {
+ if (builder.app != null) {
+ contentDescription =
+ context.getString(R.string.ongoing_privacy_chip_content_single_app,
+ builder.app?.applicationName, typesText)
+ } else {
+ contentDescription = context.getString(
+ R.string.ongoing_privacy_chip_content_multiple_apps_single_op,
+ builder.appsAndTypes.size, typesText)
+ }
}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index 1d0e16e..f6a95af 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -18,10 +18,10 @@
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
-import android.graphics.drawable.Drawable
+import android.content.Intent
+import android.content.res.ColorStateList
import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
@@ -34,29 +34,25 @@
val dialogBuilder: PrivacyDialogBuilder
) {
- val iconHeight = context.resources.getDimensionPixelSize(
+ val iconSize = context.resources.getDimensionPixelSize(
R.dimen.ongoing_appops_dialog_icon_height)
- val textMargin = context.resources.getDimensionPixelSize(
- R.dimen.ongoing_appops_dialog_text_margin)
val iconColor = context.resources.getColor(
com.android.internal.R.color.text_color_primary, context.theme)
+ companion object {
+ private const val MAX_ITEMS = 10
+ }
fun createDialog(): Dialog {
- val builder = AlertDialog.Builder(context)
- .setNeutralButton(R.string.ongoing_privacy_dialog_open_settings, null)
- if (dialogBuilder.app != null) {
- builder.setPositiveButton(R.string.ongoing_privacy_dialog_open_app,
+ val builder = AlertDialog.Builder(context).apply {
+ setNegativeButton(R.string.ongoing_privacy_dialog_cancel, null)
+ setPositiveButton(R.string.ongoing_privacy_dialog_open_settings,
object : DialogInterface.OnClickListener {
- val intent = context.packageManager
- .getLaunchIntentForPackage(dialogBuilder.app.packageName)
+ val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE)
override fun onClick(dialog: DialogInterface?, which: Int) {
Dependency.get(ActivityStarter::class.java).startActivity(intent, false)
}
})
- builder.setNegativeButton(R.string.ongoing_privacy_dialog_cancel, null)
- } else {
- builder.setPositiveButton(R.string.ongoing_privacy_dialog_okay, null)
}
builder.setView(getContentView())
return builder.create()
@@ -66,44 +62,67 @@
val layoutInflater = LayoutInflater.from(context)
val contentView = layoutInflater.inflate(R.layout.ongoing_privacy_dialog_content, null)
- val iconsContainer = contentView.findViewById(R.id.icons_container) as LinearLayout
- val textContainer = contentView.findViewById(R.id.text_container) as LinearLayout
+ val title = contentView.findViewById(R.id.title) as TextView
+ val appsList = contentView.findViewById(R.id.items_container) as LinearLayout
- addIcons(dialogBuilder, iconsContainer)
- val lm = ViewGroup.MarginLayoutParams(
- ViewGroup.MarginLayoutParams.WRAP_CONTENT,
- ViewGroup.MarginLayoutParams.WRAP_CONTENT)
- lm.topMargin = textMargin
- val now = System.currentTimeMillis()
- dialogBuilder.generateText(now).forEach {
- val text = layoutInflater.inflate(R.layout.ongoing_privacy_text_item, null) as TextView
- text.setText(it)
- textContainer.addView(text, lm)
+ title.setText(dialogBuilder.getDialogTitle())
+
+ val numItems = dialogBuilder.appsAndTypes.size
+ for (i in 0..(numItems - 1)) {
+ if (i >= MAX_ITEMS) break
+ val item = dialogBuilder.appsAndTypes[i]
+ addAppItem(appsList, item.first, item.second, dialogBuilder.types.size > 1)
}
+
+ if (numItems > MAX_ITEMS) {
+ val overflow = contentView.findViewById(R.id.overflow) as LinearLayout
+ overflow.visibility = View.VISIBLE
+ val overflowText = overflow.findViewById(R.id.app_name) as TextView
+ overflowText.text = context.resources.getQuantityString(
+ R.plurals.ongoing_privacy_dialog_overflow_text,
+ numItems - MAX_ITEMS,
+ numItems - MAX_ITEMS
+ )
+ val overflowPlus = overflow.findViewById(R.id.app_icon) as ImageView
+ overflowPlus.apply {
+ imageTintList = ColorStateList.valueOf(iconColor)
+ setImageDrawable(context.getDrawable(R.drawable.plus))
+ }
+ }
+
return contentView
}
- private fun addIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: LinearLayout) {
+ private fun addAppItem(
+ itemList: LinearLayout,
+ app: PrivacyApplication,
+ types: List<PrivacyType>,
+ showIcons: Boolean = true
+ ) {
+ val layoutInflater = LayoutInflater.from(context)
+ val item = layoutInflater.inflate(R.layout.ongoing_privacy_dialog_item, itemList, false)
+ val appIcon = item.findViewById(R.id.app_icon) as ImageView
+ val appName = item.findViewById(R.id.app_name) as TextView
+ val icons = item.findViewById(R.id.icons) as LinearLayout
- fun LinearLayout.addIcon(icon: Drawable) {
- val image = ImageView(context).apply {
- setImageDrawable(icon.apply {
- setBounds(0, 0, iconHeight, iconHeight)
- maxHeight = this@addIcon.height
- })
- adjustViewBounds = true
+ app.icon?.let {
+ appIcon.setImageDrawable(it)
+ }
+
+ appName.text = app.applicationName
+ if (showIcons) {
+ dialogBuilder.generateIconsForApp(types).forEach {
+ it.setBounds(0, 0, iconSize, iconSize)
+ val image = ImageView(context).apply {
+ imageTintList = ColorStateList.valueOf(iconColor)
+ setImageDrawable(it)
+ }
+ icons.addView(image, iconSize, LinearLayout.LayoutParams.WRAP_CONTENT)
}
- addView(image, LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.MATCH_PARENT)
+ icons.visibility = View.VISIBLE
+ } else {
+ icons.visibility = View.GONE
}
-
- dialogBuilder.generateIcons().forEach {
- it.mutate()
- it.setTint(iconColor)
- iconsContainer.addIcon(it)
- }
- dialogBuilder.app.let {
- it?.icon?.let { iconsContainer.addIcon(it) }
- }
+ itemList.addView(item)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
index 5ce4ee7..519df19 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
@@ -15,59 +15,53 @@
package com.android.systemui.privacy
import android.content.Context
+import android.graphics.drawable.Drawable
import com.android.systemui.R
-import java.lang.Math.max
class PrivacyDialogBuilder(val context: Context, itemsList: List<PrivacyItem>) {
- companion object {
- val MILLIS_IN_MINUTE: Long = 1000 * 60
- }
- private val itemsByType: Map<PrivacyType, List<PrivacyItem>>
+ val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
+ val types: List<PrivacyType>
val app: PrivacyApplication?
+ private val separator = context.getString(R.string.ongoing_privacy_dialog_separator)
+ private val lastSeparator = context.getString(R.string.ongoing_privacy_dialog_last_separator)
init {
- itemsByType = itemsList.groupBy { it.privacyType }
- val apps = itemsList.map { it.application }.distinct()
- val singleApp = apps.size == 1
- app = if (singleApp) apps.get(0) else null
+ appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
+ .toList()
+ .sortedWith(compareBy({ -it.second.size }, { it.first }))
+ types = itemsList.map { it.privacyType }.distinct().sorted()
+ val singleApp = appsAndTypes.size == 1
+ app = if (singleApp) appsAndTypes[0].first else null
}
- private fun buildTextForItem(type: PrivacyType, now: Long): String {
- val items = itemsByType.getOrDefault(type, emptyList<PrivacyItem>())
- return when (items.size) {
- 0 -> throw IllegalStateException("List cannot be empty")
- 1 -> {
- val item = items.get(0)
- val minutesUsed = max(((now - item.timeStarted) / MILLIS_IN_MINUTE).toInt(), 1)
- context.getString(R.string.ongoing_privacy_dialog_app_item,
- item.application.applicationName, type.getName(context), minutesUsed)
- }
- else -> {
- val apps = items.map { it.application.applicationName }.joinToString()
- context.getString(R.string.ongoing_privacy_dialog_apps_item,
- apps, type.getName(context))
- }
+ fun generateIconsForApp(types: List<PrivacyType>): List<Drawable> {
+ return types.sorted().map { it.getIcon(context) }
+ }
+
+ fun generateIcons() = types.map { it.getIcon(context) }
+
+ private fun <T> List<T>.joinWithAnd(): StringBuilder {
+ return subList(0, size - 1).joinTo(StringBuilder(), separator = separator).apply {
+ append(lastSeparator)
+ append(this@joinWithAnd.last())
}
}
- private fun buildTextForApp(types: Set<PrivacyType>): List<String> {
- app?.let {
- val typesText = types.map { it.getName(context) }.sorted().joinToString()
- return listOf(context.getString(R.string.ongoing_privacy_dialog_single_app,
- it.applicationName, typesText))
- } ?: throw IllegalStateException("There has to be a single app")
+ fun joinTypes(): String {
+ return when (types.size) {
+ 0 -> ""
+ 1 -> types[0].getName(context)
+ else -> types.map { it.getName(context) }.joinWithAnd().toString()
+ }
}
- fun generateText(now: Long): List<String> {
- if (app == null || itemsByType.keys.size == 1) {
- return itemsByType.keys.map { buildTextForItem(it, now) }
+ fun getDialogTitle(): String {
+ if (app != null) {
+ return context.getString(R.string.ongoing_privacy_dialog_single_app_title, joinTypes())
} else {
- return buildTextForApp(itemsByType.keys)
+ return context.getString(R.string.ongoing_privacy_dialog_multiple_apps_title,
+ joinTypes())
}
}
-
- fun generateTypesText() = itemsByType.keys.map { it.getName(context) }.sorted().joinToString()
-
- fun generateIcons() = itemsByType.keys.map { it.getIcon(context) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index f409902..85e99f0 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -23,22 +23,27 @@
typealias Privacy = PrivacyType
enum class PrivacyType(val nameId: Int, val iconId: Int) {
- TYPE_CAMERA(R.string.privacy_type_camera, com.android.internal.R.drawable.ic_camera),
+ TYPE_CAMERA(R.string.privacy_type_camera, R.drawable.stat_sys_camera),
TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location),
- TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.ic_mic_26dp);
+ TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.stat_sys_mic_none);
fun getName(context: Context) = context.resources.getString(nameId)
- fun getIcon(context: Context) = context.resources.getDrawable(iconId, null)
+ fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
}
data class PrivacyItem(
val privacyType: PrivacyType,
- val application: PrivacyApplication,
- val timeStarted: Long
+ val application: PrivacyApplication
)
-data class PrivacyApplication(val packageName: String, val context: Context) {
+data class PrivacyApplication(val packageName: String, val context: Context)
+ : Comparable<PrivacyApplication> {
+
+ override fun compareTo(other: PrivacyApplication): Int {
+ return applicationName.compareTo(other.applicationName)
+ }
+
var icon: Drawable? = null
var applicationName: String
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 5141e50..3fa3e8e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -95,7 +95,7 @@
else -> return null
}
val app = PrivacyApplication(appOpItem.packageName, context)
- return PrivacyItem(type, app, appOpItem.timeStarted)
+ return PrivacyItem(type, app)
}
// Used by containing class to get notified of changes
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index e3f85d9..427f638 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -325,15 +325,10 @@
newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor);
mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
-
- MarginLayoutParams lm = (MarginLayoutParams) mPrivacyChip.getLayoutParams();
- int sideMargins = lm.leftMargin;
- int topBottomMargins = (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
- ? 0 : sideMargins;
- lm.setMargins(sideMargins, topBottomMargins, sideMargins, topBottomMargins);
- mPrivacyChip.setLayoutParams(lm);
}
+
+
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
@@ -378,6 +373,15 @@
setLayoutParams(lp);
+ if (mPrivacyChip != null) {
+ MarginLayoutParams lm = (MarginLayoutParams) mPrivacyChip.getLayoutParams();
+ int sideMargins = lm.leftMargin;
+ int topBottomMargins = resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_top_chip_margin);
+ lm.setMargins(sideMargins, topBottomMargins, sideMargins, topBottomMargins);
+ mPrivacyChip.setLayoutParams(lm);
+ }
+
updateStatusIconAlphaAnimator();
updateHeaderTextContainerAlphaAnimator();
}
@@ -729,7 +733,8 @@
public void setMargins(int sideMargins) {
for (int i = 0; i < getChildCount(); i++) {
View v = getChildAt(i);
- if (v == mSystemIconsView || v == mQuickQsStatusIcons || v == mHeaderQsPanel) {
+ if (v == mSystemIconsView || v == mQuickQsStatusIcons || v == mHeaderQsPanel
+ || v == mPrivacyChip) {
continue;
}
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) v.getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index f42c6ef..f23ae3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -53,7 +53,8 @@
public static NotificationUiAdjustment extractFromNotificationEntry(
NotificationData.Entry entry) {
- return new NotificationUiAdjustment(entry.key, entry.smartActions, entry.smartReplies);
+ return new NotificationUiAdjustment(
+ entry.key, entry.systemGeneratedSmartActions, entry.smartReplies);
}
public static boolean needReinflate(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index 4e712a5..da6d977 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -114,8 +114,9 @@
public CharSequence remoteInputText;
public List<SnoozeCriterion> snoozeCriteria;
public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
+ /** Smart Actions provided by the NotificationAssistantService. */
@NonNull
- public List<Notification.Action> smartActions = Collections.emptyList();
+ public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
public CharSequence[] smartReplies = new CharSequence[0];
private int mCachedContrastColor = COLOR_INVALID;
@@ -171,7 +172,7 @@
importance = ranking.getImportance();
snoozeCriteria = ranking.getSnoozeCriteria();
userSentiment = ranking.getUserSentiment();
- smartActions = ranking.getSmartActions() == null
+ systemGeneratedSmartActions = ranking.getSmartActions() == null
? Collections.emptyList() : ranking.getSmartActions();
smartReplies = ranking.getSmartReplies() == null
? new CharSequence[0]
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 3bea7db..274d4b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -702,7 +702,6 @@
&& !mPresenter.isPresenterFullyCollapsed();
row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
- row.setSmartActions(entry.smartActions);
row.setEntry(entry);
row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, shouldHeadsUp(entry));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 5166e06..c7876cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -39,7 +39,6 @@
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Context;
import android.content.pm.PackageInfo;
@@ -1567,10 +1566,6 @@
mNotificationInflater.setUsesIncreasedHeight(use);
}
- public void setSmartActions(List<Notification.Action> smartActions) {
- mNotificationInflater.setSmartActions(smartActions);
- }
-
public void setUseIncreasedHeadsUpHeight(boolean use) {
mUseIncreasedHeadsUpHeight = use;
mNotificationInflater.setUsesIncreasedHeadsUpHeight(use);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 38d6b35..e1c2f73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -43,10 +43,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
@@ -131,7 +128,6 @@
private boolean mIsChildInGroup;
private InflationCallback mCallback;
private boolean mRedactAmbient;
- private List<Notification.Action> mSmartActions;
private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
public NotificationInflater(ExpandableNotificationRow row) {
@@ -161,10 +157,6 @@
mUsesIncreasedHeight = usesIncreasedHeight;
}
- public void setSmartActions(List<Notification.Action> smartActions) {
- mSmartActions = smartActions;
- }
-
public void setUsesIncreasedHeadsUpHeight(boolean usesIncreasedHeight) {
mUsesIncreasedHeadsUpHeight = usesIncreasedHeight;
}
@@ -258,8 +250,7 @@
StatusBarNotification sbn = mRow.getEntry().notification;
AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews,
mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
- mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler,
- mSmartActions);
+ mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler);
if (mCallback != null && mCallback.doInflateSynchronous()) {
task.onPostExecute(task.doInBackground());
} else {
@@ -765,15 +756,13 @@
private Exception mError;
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
private CancellationSignal mCancellationSignal;
- private List<Notification.Action> mSmartActions;
private AsyncInflationTask(StatusBarNotification notification,
@InflationFlag int reInflateFlags,
ArrayMap<Integer, RemoteViews> cachedContentViews, ExpandableNotificationRow row,
boolean isLowPriority, boolean isChildInGroup, boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
- InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler,
- List<Notification.Action> smartActions) {
+ InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler) {
mRow = row;
mSbn = notification;
mReInflateFlags = reInflateFlags;
@@ -786,9 +775,6 @@
mRedactAmbient = redactAmbient;
mRemoteViewClickHandler = remoteViewClickHandler;
mCallback = callback;
- mSmartActions = smartActions == null
- ? Collections.emptyList()
- : new ArrayList<>(smartActions);
NotificationData.Entry entry = row.getEntry();
entry.setInflationTask(this);
}
@@ -806,8 +792,6 @@
= Notification.Builder.recoverBuilder(mContext,
mSbn.getNotification());
- applyChanges(recoveredBuilder);
-
Context packageContext = mSbn.getPackageContext(mContext);
Notification notification = mSbn.getNotification();
if (notification.isMediaNotification()) {
@@ -834,18 +818,6 @@
}
}
- /**
- * Apply changes to the given notification builder, like adding smart actions suggested by
- * a {@link android.service.notification.NotificationAssistantService}.
- */
- private void applyChanges(Notification.Builder builder) {
- if (mSmartActions != null) {
- for (Notification.Action smartAction : mSmartActions) {
- builder.addAction(smartAction);
- }
- }
- }
-
private void handleError(Exception e) {
mRow.getEntry().onInflationTaskFinished();
StatusBarNotification sbn = mRow.getStatusBarNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index a7329b0..ff31b26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -401,6 +401,8 @@
*/
private float mBackgroundXFactor = 1f;
+ private boolean mSwipingInProgress;
+
private boolean mUsingLightTheme;
private boolean mQsExpanded;
private boolean mForwardScrollable;
@@ -3286,7 +3288,7 @@
|| ev.getActionMasked() == MotionEvent.ACTION_UP;
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
- boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+ boolean swipingInProgress = mSwipingInProgress;
if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
if (isCancelOrUp) {
mExpandHelper.onlyObserveMovements(false);
@@ -3341,7 +3343,7 @@
@Override
@ShadeViewRefactor(RefactorComponent.INPUT)
public boolean onGenericMotionEvent(MotionEvent event) {
- if (!isScrollingEnabled() || !mIsExpanded || mSwipeHelper.isSwipingInProgress() || mExpandingNotification
+ if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
|| mDisallowScrollingInThisMotion) {
return false;
}
@@ -3568,7 +3570,7 @@
initDownStates(ev);
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
- boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+ boolean swipingInProgress = mSwipingInProgress;
if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
}
@@ -3847,6 +3849,14 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void setSwipingInProgress(boolean swiping) {
+ mSwipingInProgress = swiping;
+ if (swiping) {
+ requestDisallowInterceptTouchEvent(true);
+ }
+ }
+
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onWindowFocusChanged(boolean hasWindowFocus) {
@@ -5642,6 +5652,7 @@
@Override
public void onDragCancelled(View v) {
+ setSwipingInProgress(false);
mFalsingManager.onNotificatonStopDismissing();
}
@@ -5669,6 +5680,7 @@
*/
public void handleChildViewDismissed(View view) {
+ setSwipingInProgress(false);
if (mDismissAllInProgress) {
return;
}
@@ -5737,6 +5749,7 @@
@Override
public void onBeginDrag(View v) {
mFalsingManager.onNotificatonStartDismissing();
+ setSwipingInProgress(true);
mAmbientState.onBeginDrag(v);
updateContinuousShadowDrawing();
if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 4357a51..f1d9549 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -231,7 +231,6 @@
if (mCallback.isExpanded()) {
// We don't want to quick-dismiss when it's a heads up as this might lead to closing
// of the panel early.
- mSwipingInProgress = false;
mCallback.handleChildViewDismissed(view);
}
mCallback.onDismiss();
@@ -251,7 +250,6 @@
@Override
public void snapChild(final View animView, final float targetLeft, float velocity) {
superSnapChild(animView, targetLeft, velocity);
- mSwipingInProgress = false;
mCallback.onDragCancelled(animView);
if (targetLeft == 0) {
handleMenuCoveredOrDismissed();
@@ -358,7 +356,6 @@
public void onMenuShown(View animView) {
setExposedMenuView(getTranslatingParentView());
- mSwipingInProgress = false;
mCallback.onDragCancelled(animView);
Handler handler = getHandler();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 0e6efc8..c84f3db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -66,6 +66,8 @@
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.UiOffloadThread;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -101,7 +103,8 @@
*/
public class PhoneStatusBarPolicy implements Callback, Callbacks,
RotationLockControllerCallback, Listener, LocationChangeCallback,
- ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback {
+ ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback,
+ PrivacyItemController.Callback {
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -120,6 +123,8 @@
private final String mSlotHeadset;
private final String mSlotDataSaver;
private final String mSlotLocation;
+ private final String mSlotMicrophone;
+ private final String mSlotCamera;
private final Context mContext;
private final Handler mHandler = new Handler();
@@ -136,6 +141,7 @@
private final DeviceProvisionedController mProvisionedController;
private final KeyguardMonitor mKeyguardMonitor;
private final LocationController mLocationController;
+ private final PrivacyItemController mPrivacyItemController;
private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
@@ -169,6 +175,7 @@
mProvisionedController = Dependency.get(DeviceProvisionedController.class);
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mLocationController = Dependency.get(LocationController.class);
+ mPrivacyItemController = new PrivacyItemController(mContext, this);
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
@@ -183,6 +190,8 @@
mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
+ mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
+ mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
// listen for broadcasts
IntentFilter filter = new IntentFilter();
@@ -241,6 +250,12 @@
context.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
+ // privacy items
+ mIconController.setIcon(mSlotMicrophone, R.drawable.stat_sys_mic_none, null);
+ mIconController.setIconVisibility(mSlotMicrophone, false);
+ mIconController.setIcon(mSlotCamera, R.drawable.stat_sys_camera, null);
+ mIconController.setIconVisibility(mSlotCamera, false);
+
mRotationLockController.addCallback(this);
mBluetooth.addCallback(this);
mProvisionedController.addCallback(this);
@@ -251,6 +266,7 @@
mDataSaver.addCallback(this);
mKeyguardMonitor.addCallback(this);
mLocationController.addCallback(this);
+ mPrivacyItemController.setListening(true);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
@@ -279,6 +295,7 @@
mDataSaver.removeCallback(this);
mKeyguardMonitor.removeCallback(this);
mLocationController.removeCallback(this);
+ mPrivacyItemController.setListening(false);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
mContext.unregisterReceiver(mIntentReceiver);
@@ -798,6 +815,34 @@
mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
}
+ @Override // PrivacyItemController.Callback
+ public void privacyChanged(List<PrivacyItem> privacyItems) {
+ updatePrivacyItems(privacyItems);
+ }
+
+ private void updatePrivacyItems(List<PrivacyItem> items) {
+ boolean showCamera = false;
+ boolean showMicrophone = false;
+ boolean showLocation = false;
+ for (PrivacyItem item : items) {
+ switch (item.getPrivacyType()) {
+ case TYPE_CAMERA:
+ showCamera = true;
+ break;
+ case TYPE_LOCATION:
+ showLocation = true;
+ break;
+ case TYPE_MICROPHONE:
+ showMicrophone = true;
+ break;
+ }
+ }
+
+ mIconController.setIconVisibility(mSlotCamera, showCamera);
+ mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
+ mIconController.setIconVisibility(mSlotLocation, showLocation);
+ }
+
private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
index 7204d31..b23f667 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
@@ -27,55 +27,28 @@
@SmallTest
class PrivacyDialogBuilderTest : SysuiTestCase() {
- companion object {
- val MILLIS_IN_MINUTE: Long = 1000 * 60
- val NOW = 4 * MILLIS_IN_MINUTE
- }
-
@Test
- fun testGenerateText_multipleApps() {
+ fun testGenerateAppsList() {
val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
- "Bar", context), 2 * MILLIS_IN_MINUTE)
+ "Bar", context))
val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
- "Bar", context), 3 * MILLIS_IN_MINUTE)
+ "Bar", context))
val foo0 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
- "Foo", context), 0)
+ "Foo", context))
val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
- "Baz", context), 1 * MILLIS_IN_MINUTE)
+ "Baz", context))
val items = listOf(bar2, foo0, baz1, bar3)
val textBuilder = PrivacyDialogBuilder(context, items)
- val textList = textBuilder.generateText(NOW)
- assertEquals(2, textList.size)
- assertEquals("Bar, Foo, Baz are using your camera", textList[0])
- assertEquals("Bar is using your location for the last 1 min", textList[1])
- }
-
- @Test
- fun testGenerateText_singleApp() {
- val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
- "Bar", context), 0)
- val bar1 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
- "Bar", context), 0)
-
- val items = listOf(bar2, bar1)
-
- val textBuilder = PrivacyDialogBuilder(context, items)
- val textList = textBuilder.generateText(NOW)
- assertEquals(1, textList.size)
- assertEquals("Bar is using your camera, location", textList[0])
- }
-
- @Test
- fun testGenerateText_singleApp_singleType() {
- val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
- "Bar", context), 2 * MILLIS_IN_MINUTE)
- val items = listOf(bar2)
- val textBuilder = PrivacyDialogBuilder(context, items)
- val textList = textBuilder.generateText(NOW)
- assertEquals(1, textList.size)
- assertEquals("Bar is using your camera for the last 2 min", textList[0])
+ val list = textBuilder.appsAndTypes
+ assertEquals(3, list.size)
+ val appsList = list.map { it.first }
+ val typesList = list.map { it.second }
+ assertEquals(listOf("Bar", "Baz", "Foo"), appsList.map { it.packageName })
+ assertEquals(listOf(Privacy.TYPE_CAMERA, Privacy.TYPE_LOCATION), typesList[0])
+ assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
+ assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[2])
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index 8e6bfe3..f59bfae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -72,6 +72,8 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -390,10 +392,16 @@
@Test
public void testCreateNotificationDataEntry_RankingUpdate() {
Ranking ranking = mock(Ranking.class);
+ initStatusBarNotification(false);
- ArrayList<Notification.Action> smartActions = new ArrayList<>();
- smartActions.add(createAction());
- when(ranking.getSmartActions()).thenReturn(smartActions);
+ List<Notification.Action> appGeneratedSmartActions =
+ Collections.singletonList(createContextualAction("appGeneratedAction"));
+ mMockStatusBarNotification.getNotification().actions =
+ appGeneratedSmartActions.toArray(new Notification.Action[0]);
+
+ List<Notification.Action> systemGeneratedSmartActions =
+ Collections.singletonList(createAction("systemGeneratedAction"));
+ when(ranking.getSmartActions()).thenReturn(systemGeneratedSmartActions);
when(ranking.getChannel()).thenReturn(NOTIFICATION_CHANNEL);
@@ -407,7 +415,7 @@
NotificationData.Entry entry =
new NotificationData.Entry(mMockStatusBarNotification, ranking);
- assertEquals(smartActions, entry.smartActions);
+ assertEquals(systemGeneratedSmartActions, entry.systemGeneratedSmartActions);
assertEquals(NOTIFICATION_CHANNEL, entry.channel);
assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.userSentiment);
assertEquals(snoozeCriterions, entry.snoozeCriteria);
@@ -459,10 +467,20 @@
}
}
- private Notification.Action createAction() {
+ private Notification.Action createContextualAction(String title) {
return new Notification.Action.Builder(
Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
- "action",
+ title,
+ PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0))
+ .setSemanticAction(
+ Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION)
+ .build();
+ }
+
+ private Notification.Action createAction(String title) {
+ return new Notification.Action.Builder(
+ Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+ title,
PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 9f8a5cc..d1fe5af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -438,8 +438,8 @@
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow).setEntry(eq(mEntry));
- assertEquals(1, mEntry.smartActions.size());
- assertEquals("action", mEntry.smartActions.get(0).title);
+ assertEquals(1, mEntry.systemGeneratedSmartActions.size());
+ assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
}
@Test
@@ -453,7 +453,7 @@
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow, never()).setEntry(eq(mEntry));
- assertEquals(0, mEntry.smartActions.size());
+ assertEquals(0, mEntry.systemGeneratedSmartActions.size());
}
@Test
@@ -467,8 +467,8 @@
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow, never()).setEntry(eq(mEntry));
- assertEquals(1, mEntry.smartActions.size());
- assertEquals("action", mEntry.smartActions.get(0).title);
+ assertEquals(1, mEntry.systemGeneratedSmartActions.size());
+ assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
}
@Test
@@ -482,8 +482,8 @@
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow, never()).setEntry(eq(mEntry));
- assertEquals(1, mEntry.smartActions.size());
- assertEquals("action", mEntry.smartActions.get(0).title);
+ assertEquals(1, mEntry.systemGeneratedSmartActions.size());
+ assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
}
private Notification.Action createAction() {
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 8ae5872..3e07d12 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -844,6 +844,8 @@
// PACKAGE: App that posted the notification
// DETAIL: Notification is expanded by user.
// PACKAGE: App that posted the notification
+ // COLLAPSE: Notification is collapsed by user.
+ // PACKAGE: App that posted the notification
// DISMISS: Notification is dismissed.
// PACKAGE: App that posted the notification
// SUBTYPE: Dismiss reason from NotificationManagerService.java
@@ -6596,6 +6598,12 @@
// OS: Q
NOTIFICATION_ZEN_MODE_OVERRIDING_APP = 1589;
+ // ACTION: User sent a direct reply
+ // PACKAGE: App that posted the notification
+ // CATEGORY: NOTIFICATION
+ // OS: Q
+ NOTIFICATION_DIRECT_REPLY_ACTION = 1590;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 4205ac7..31238df 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -166,12 +166,12 @@
context.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
}
- @Override // from MasterSystemService
+ @Override // from AbstractMasterSystemService
protected String getServiceSettingsProperty() {
return Settings.Secure.AUTOFILL_SERVICE;
}
- @Override // from MasterSystemService
+ @Override // from AbstractMasterSystemService
protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
@NonNull ContentObserver observer) {
resolver.registerContentObserver(Settings.Global.getUriFor(
@@ -188,7 +188,7 @@
UserHandle.USER_ALL);
}
- @Override // from MasterSystemService
+ @Override // from AbstractMasterSystemService
protected void onSettingsChanged(int userId, @NonNull String property) {
switch (property) {
case Settings.Global.AUTOFILL_LOGGING_LEVEL:
@@ -210,25 +210,24 @@
}
}
- @Override // from MasterSystemService
- protected AutofillManagerServiceImpl newServiceLocked(int resolvedUserId, boolean disabled) {
+ @Override // from AbstractMasterSystemService
+ protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
+ boolean disabled) {
return new AutofillManagerServiceImpl(this, mLock, mRequestsHistory,
mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi, mAutofillCompatState,
disabled);
}
- @Override // MasterSystemService
- protected AutofillManagerServiceImpl removeCachedServiceLocked(int userId) {
- final AutofillManagerServiceImpl service = super.removeCachedServiceLocked(userId);
- if (service != null) {
- service.destroyLocked();
- mAutofillCompatState.removeCompatibilityModeRequests(userId);
- }
- return service;
+ @Override // AbstractMasterSystemService
+ protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service,
+ @UserIdInt int userId) {
+ service.destroyLocked();
+ mAutofillCompatState.removeCompatibilityModeRequests(userId);
}
- @Override // from MasterSystemService
- protected void onServiceEnabledLocked(@NonNull AutofillManagerServiceImpl service, int userId) {
+ @Override // from AbstractMasterSystemService
+ protected void onServiceEnabledLocked(@NonNull AutofillManagerServiceImpl service,
+ @UserIdInt int userId) {
addCompatibilityModeRequestsLocked(service, userId);
}
@@ -245,7 +244,7 @@
}
// Called by Shell command.
- void destroySessions(int userId, IResultReceiver receiver) {
+ void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
Slog.i(TAG, "destroySessions() for userId " + userId);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 1ad83ec..c12a5e7 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -104,7 +104,6 @@
import com.android.server.backup.fullbackup.FullBackupEntry;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.internal.BackupHandler;
-import com.android.server.backup.keyvalue.BackupRequest;
import com.android.server.backup.internal.ClearDataObserver;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.internal.Operation;
@@ -112,6 +111,7 @@
import com.android.server.backup.internal.ProvisionedObserver;
import com.android.server.backup.internal.RunBackupReceiver;
import com.android.server.backup.internal.RunInitializeReceiver;
+import com.android.server.backup.keyvalue.BackupRequest;
import com.android.server.backup.params.AdbBackupParams;
import com.android.server.backup.params.AdbParams;
import com.android.server.backup.params.AdbRestoreParams;
@@ -160,7 +160,6 @@
import java.util.concurrent.atomic.AtomicInteger;
public class BackupManagerService {
-
public static final String TAG = "BackupManagerService";
public static final boolean DEBUG = true;
public static final boolean MORE_DEBUG = false;
@@ -170,6 +169,9 @@
// nonzero == enabled. File missing or contains a zero byte == disabled.
private static final String BACKUP_ENABLE_FILE = "backup_enabled";
+ // Persistently track the need to do a full init.
+ private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
+
// System-private key used for backing up an app's widget state. Must
// begin with U+FFxx by convention (we reserve all keys starting
// with U+FF00 or higher for system use).
@@ -196,11 +198,16 @@
public static final int BACKUP_METADATA_VERSION = 1;
public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
- private static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
+ private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
+
+ // Round-robin queue for scheduling full backup passes.
+ private static final int SCHEDULE_FILE_VERSION = 1;
public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
- private static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
+
+ // Pseudoname that we use for the Package Manager metadata "package".
+ public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
// Retry interval for clear/init when the transport is unavailable
private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
@@ -210,6 +217,21 @@
public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+ // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
+ // pending operations list.
+ public static final int OP_PENDING = 0;
+ private static final int OP_ACKNOWLEDGED = 1;
+ private static final int OP_TIMEOUT = -1;
+
+ // Waiting for backup agent to respond during backup operation.
+ public static final int OP_TYPE_BACKUP_WAIT = 0;
+
+ // Waiting for backup agent to respond during restore operation.
+ public static final int OP_TYPE_RESTORE_WAIT = 1;
+
+ // An entire backup operation spanning multiple packages.
+ public static final int OP_TYPE_BACKUP = 2;
+
// Time delay for initialization operations that can be delayed so as not to consume too much CPU
// on bring-up and increase time-to-UI.
private static final long INITIALIZATION_DELAY_MILLIS = 3000;
@@ -226,8 +248,62 @@
private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
- private BackupManagerConstants mConstants;
+ // The published binder is a singleton Trampoline object that calls through to the proper code.
+ // This indirection lets us turn down the heavy implementation object on the fly without
+ // disturbing binders that have been cached elsewhere in the system.
+ private static Trampoline sInstance;
+
+ static Trampoline getInstance() {
+ // Always constructed during system bring up, so no need to lazy-init.
+ return sInstance;
+ }
+
+ /** Helper to create the {@link BackupManagerService} instance. */
+ public static BackupManagerService create(
+ Context context,
+ Trampoline parent,
+ HandlerThread backupThread) {
+ // Set up our transport options and initialize the default transport
+ SystemConfig systemConfig = SystemConfig.getInstance();
+ Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
+ if (transportWhitelist == null) {
+ transportWhitelist = Collections.emptySet();
+ }
+
+ String transport =
+ Settings.Secure.getString(
+ context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+ if (TextUtils.isEmpty(transport)) {
+ transport = null;
+ }
+ if (DEBUG) {
+ Slog.v(TAG, "Starting with transport " + transport);
+ }
+ TransportManager transportManager =
+ new TransportManager(
+ context,
+ transportWhitelist,
+ transport);
+
+ // If encrypted file systems is enabled or disabled, this call will return the
+ // correct directory.
+ File baseStateDir = new File(Environment.getDataDirectory(), "backup");
+
+ // This dir on /cache is managed directly in init.rc
+ File dataDir = new File(Environment.getDownloadCacheDirectory(), "backup_stage");
+
+ return new BackupManagerService(
+ context,
+ parent,
+ backupThread,
+ baseStateDir,
+ dataDir,
+ transportManager);
+ }
+
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+ private final TransportManager mTransportManager;
+
private Context mContext;
private PackageManager mPackageManager;
private IPackageManager mPackageManagerBinder;
@@ -235,20 +311,21 @@
private PowerManager mPowerManager;
private AlarmManager mAlarmManager;
private IStorageManager mStorageManager;
+ private BackupManagerConstants mConstants;
+ private PowerManager.WakeLock mWakelock;
+ private BackupHandler mBackupHandler;
private IBackupManager mBackupManagerBinder;
- private final TransportManager mTransportManager;
-
private boolean mEnabled; // access to this is synchronized on 'this'
private boolean mProvisioned;
private boolean mAutoRestore;
- private PowerManager.WakeLock mWakelock;
- private BackupHandler mBackupHandler;
+
private PendingIntent mRunBackupIntent;
private PendingIntent mRunInitIntent;
- private BroadcastReceiver mRunBackupReceiver;
- private BroadcastReceiver mRunInitReceiver;
+
+ private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
+
// map UIDs to the set of participating packages under that UID
private final SparseArray<HashSet<String>> mBackupParticipants
= new SparseArray<>();
@@ -257,9 +334,6 @@
private HashMap<String, BackupRequest> mPendingBackups
= new HashMap<>();
- // Pseudoname that we use for the Package Manager metadata "package"
- public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
-
// locking around the pending-backup management
private final Object mQueueLock = new Object();
@@ -269,25 +343,32 @@
// completed.
private final Object mAgentConnectLock = new Object();
private IBackupAgent mConnectedAgent;
- private volatile boolean mBackupRunning;
private volatile boolean mConnecting;
- private volatile long mLastBackupPass;
- // For debugging, we maintain a progress trace of operations during backup
- public static final boolean DEBUG_BACKUP_TRACE = true;
- private final List<String> mBackupTrace = new ArrayList<>();
+ private volatile boolean mBackupRunning;
+ private volatile long mLastBackupPass;
// A similar synchronization mechanism around clearing apps' data for restore
private final Object mClearDataLock = new Object();
private volatile boolean mClearingData;
+ // Used by ADB.
private final BackupPasswordManager mBackupPasswordManager;
+ private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
+ private final SecureRandom mRng = new SecureRandom();
// Time when we post the transport registration operation
private final long mRegisterTransportsRequestedTime;
+ @GuardedBy("mQueueLock")
+ private PerformFullTransportBackupTask mRunningFullBackupTask;
+
+ @GuardedBy("mQueueLock")
+ private ArrayList<FullBackupEntry> mFullBackupQueue;
+
@GuardedBy("mPendingRestores")
private boolean mIsRestoreInProgress;
+
@GuardedBy("mPendingRestores")
private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
@@ -296,17 +377,155 @@
// Watch the device provisioning operation during setup
private ContentObserver mProvisionedObserver;
- // The published binder is actually to a singleton trampoline object that calls
- // through to the proper code. This indirection lets us turn down the heavy
- // implementation object on the fly without disturbing binders that have been
- // cached elsewhere in the system.
- static Trampoline sInstance;
+ /**
+ * mCurrentOperations contains the list of currently active operations.
+ *
+ * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
+ * An operation wraps a BackupRestoreTask within it.
+ * It's the responsibility of this task to remove the operation from this array.
+ *
+ * A BackupRestore task gets notified of ack/timeout for the operation via
+ * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
+ * on the mCurrentOpLock.
+ * {@link BackupManagerService#waitUntilOperationComplete(int)} is
+ * used in various places to 'wait' for notifyAll and detect change of pending state of an
+ * operation. So typically, an operation will be removed from this array by:
+ * - BackupRestoreTask#handleCancel and
+ * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
+ * these places because waitUntilOperationComplete relies on the operation being present to
+ * determine its completion status.
+ *
+ * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
+ * cancel backup tasks.
+ */
+ @GuardedBy("mCurrentOpLock")
+ private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
+ private final Object mCurrentOpLock = new Object();
+ private final Random mTokenGenerator = new Random();
+ final AtomicInteger mNextToken = new AtomicInteger();
- static Trampoline getInstance() {
- // Always constructed during system bringup, so no need to lazy-init
- return sInstance;
+ // Where we keep our journal files and other bookkeeping.
+ private File mBaseStateDir;
+ private File mDataDir;
+ private File mJournalDir;
+ @Nullable
+ private DataChangedJournal mJournal;
+ private File mFullBackupScheduleFile;
+
+ // Keep a log of all the apps we've ever backed up.
+ private ProcessedPackagesJournal mProcessedPackagesJournal;
+
+ private File mTokenFile;
+ private Set<String> mAncestralPackages = null;
+ private long mAncestralToken = 0;
+ private long mCurrentToken = 0;
+
+ @VisibleForTesting
+ public BackupManagerService(
+ Context context,
+ Trampoline parent,
+ HandlerThread backupThread,
+ File baseStateDir,
+ File dataDir,
+ TransportManager transportManager) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mPackageManagerBinder = AppGlobals.getPackageManager();
+ mActivityManager = ActivityManager.getService();
+
+ mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
+
+ mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+
+ mAgentTimeoutParameters = new
+ BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+ mAgentTimeoutParameters.start();
+
+ // spin up the backup/restore handler thread
+ mBackupHandler = new BackupHandler(this, backupThread.getLooper());
+
+ // Set up our bookkeeping
+ final ContentResolver resolver = context.getContentResolver();
+ mProvisioned = Settings.Global.getInt(resolver,
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ mAutoRestore = Settings.Secure.getInt(resolver,
+ Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+
+ mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mProvisionedObserver);
+
+ mBaseStateDir = baseStateDir;
+ mBaseStateDir.mkdirs();
+ if (!SELinux.restorecon(mBaseStateDir)) {
+ Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+ }
+
+ mDataDir = dataDir;
+
+ mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
+
+ // Alarm receivers for scheduled backups & initialization operations
+ BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(RUN_BACKUP_ACTION);
+ context.registerReceiver(mRunBackupReceiver, filter,
+ android.Manifest.permission.BACKUP, null);
+
+ BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
+ filter = new IntentFilter();
+ filter.addAction(RUN_INITIALIZE_ACTION);
+ context.registerReceiver(mRunInitReceiver, filter,
+ android.Manifest.permission.BACKUP, null);
+
+ Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
+ backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
+
+ Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
+ initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
+
+ // Set up the backup-request journaling
+ mJournalDir = new File(mBaseStateDir, "pending");
+ mJournalDir.mkdirs(); // creates mBaseStateDir along the way
+ mJournal = null; // will be created on first use
+
+ mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
+ // We are observing changes to the constants throughout the lifecycle of BMS. This is
+ // because we reference the constants in multiple areas of BMS, which otherwise would
+ // require frequent starting and stopping.
+ mConstants.start();
+
+ // Set up the various sorts of package tracking we do
+ mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
+ initPackageTracking();
+
+ // Build our mapping of uid to backup client services. This implicitly
+ // schedules a backup pass on the Package Manager metadata the first
+ // time anything needs to be backed up.
+ synchronized (mBackupParticipants) {
+ addPackageParticipantsLocked(null);
+ }
+
+ mTransportManager = transportManager;
+ mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
+ mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
+ mBackupHandler.postDelayed(
+ mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
+
+ // Now that we know about valid backup participants, parse any leftover journal files into
+ // the pending backup set
+ mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
+
+ // Power management
+ mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
}
+
public BackupManagerConstants getConstants() {
return mConstants;
}
@@ -549,6 +768,7 @@
return mPendingInits;
}
+ /** Clear all pending transport initializations. */
public void clearPendingInits() {
mPendingInits.clear();
}
@@ -562,28 +782,10 @@
mRunningFullBackupTask = runningFullBackupTask;
}
- public static final class Lifecycle extends SystemService {
-
- public Lifecycle(Context context) {
- super(context);
- sInstance = new Trampoline(context);
- }
-
- @Override
- public void onStart() {
- publishBinderService(Context.BACKUP_SERVICE, sInstance);
- }
-
- @Override
- public void onUnlockUser(int userId) {
- if (userId == UserHandle.USER_SYSTEM) {
- sInstance.unlockSystemUser();
- }
- }
- }
-
- // Called through the trampoline from onUnlockUser(), then we buck the work
- // off to the background thread to keep the unlock time down.
+ /**
+ * Called through Trampoline from {@link Lifecycle#onUnlockUser(int)}. We run the heavy work on
+ * a background thread to keep the unlock time down.
+ */
public void unlockSystemUser() {
// Migrate legacy setting
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
@@ -618,89 +820,10 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- // Bookkeeping of in-flight operations for timeout etc. purposes. The operation
- // token is the index of the entry in the pending-operations list.
- public static final int OP_PENDING = 0;
- private static final int OP_ACKNOWLEDGED = 1;
- private static final int OP_TIMEOUT = -1;
-
- // Waiting for backup agent to respond during backup operation.
- public static final int OP_TYPE_BACKUP_WAIT = 0;
-
- // Waiting for backup agent to respond during restore operation.
- public static final int OP_TYPE_RESTORE_WAIT = 1;
-
- // An entire backup operation spanning multiple packages.
- public static final int OP_TYPE_BACKUP = 2;
-
/**
- * mCurrentOperations contains the list of currently active operations.
- *
- * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
- * An operation wraps a BackupRestoreTask within it.
- * It's the responsibility of this task to remove the operation from this array.
- *
- * A BackupRestore task gets notified of ack/timeout for the operation via
- * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
- * on the mCurrentOpLock.
- * {@link BackupManagerService#waitUntilOperationComplete(int)} is
- * used in various places to 'wait' for notifyAll and detect change of pending state of an
- * operation. So typically, an operation will be removed from this array by:
- * - BackupRestoreTask#handleCancel and
- * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
- * these places because waitUntilOperationComplete relies on the operation being present to
- * determine its completion status.
- *
- * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
- * cancel backup tasks.
+ * Utility: build a new random integer token. The low bits are the ordinal of the operation for
+ * near-time uniqueness, and the upper bits are random for app-side unpredictability.
*/
- @GuardedBy("mCurrentOpLock")
- private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
- private final Object mCurrentOpLock = new Object();
- private final Random mTokenGenerator = new Random();
- final AtomicInteger mNextToken = new AtomicInteger();
-
- private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
-
- // Where we keep our journal files and other bookkeeping
- private File mBaseStateDir;
- private File mDataDir;
- private File mJournalDir;
- @Nullable
- private DataChangedJournal mJournal;
-
- private final SecureRandom mRng = new SecureRandom();
-
- // Keep a log of all the apps we've ever backed up, and what the dataset tokens are for both
- // the current backup dataset and the ancestral dataset.
- private ProcessedPackagesJournal mProcessedPackagesJournal;
-
- private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
- // increment when the schema changes
- private File mTokenFile;
- private Set<String> mAncestralPackages = null;
- private long mAncestralToken = 0;
- private long mCurrentToken = 0;
-
- // Persistently track the need to do a full init
- private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
- private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
-
- // Round-robin queue for scheduling full backup passes
- private static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
-
- private File mFullBackupScheduleFile;
- // If we're running a schedule-driven full backup, this is the task instance doing it
-
- @GuardedBy("mQueueLock")
- private PerformFullTransportBackupTask mRunningFullBackupTask;
-
- @GuardedBy("mQueueLock")
- private ArrayList<FullBackupEntry> mFullBackupQueue;
-
- // Utility: build a new random integer token. The low bits are the ordinal of the
- // operation for near-time uniqueness, and the upper bits are random for app-
- // side unpredictability.
public int generateRandomIntegerToken() {
int token = mTokenGenerator.nextInt();
if (token < 0) token = -token;
@@ -709,10 +832,9 @@
return token;
}
- /*
- * Construct a backup agent instance for the metadata pseudopackage. This is a
- * process-local non-lifecycle agent instance, so we manually set up the context
- * topology for it.
+ /**
+ * Construct a backup agent instance for the metadata pseudopackage. This is a process-local
+ * non-lifecycle agent instance, so we manually set up the context topology for it.
*/
public BackupAgent makeMetadataAgent() {
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
@@ -721,8 +843,8 @@
return pmAgent;
}
- /*
- * Same as above but with the explicit package-set configuration.
+ /**
+ * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
*/
public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
PackageManagerBackupAgent pmAgent =
@@ -732,172 +854,6 @@
return pmAgent;
}
- // ----- Debug-only backup operation trace -----
- public void addBackupTrace(String s) {
- if (DEBUG_BACKUP_TRACE) {
- synchronized (mBackupTrace) {
- mBackupTrace.add(s);
- }
- }
- }
-
- public void clearBackupTrace() {
- if (DEBUG_BACKUP_TRACE) {
- synchronized (mBackupTrace) {
- mBackupTrace.clear();
- }
- }
- }
-
- // ----- Main service implementation -----
-
- public static BackupManagerService create(
- Context context,
- Trampoline parent,
- HandlerThread backupThread) {
- // Set up our transport options and initialize the default transport
- SystemConfig systemConfig = SystemConfig.getInstance();
- Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
- if (transportWhitelist == null) {
- transportWhitelist = Collections.emptySet();
- }
-
- String transport =
- Settings.Secure.getString(
- context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
- if (TextUtils.isEmpty(transport)) {
- transport = null;
- }
- if (DEBUG) {
- Slog.v(TAG, "Starting with transport " + transport);
- }
- TransportManager transportManager =
- new TransportManager(
- context,
- transportWhitelist,
- transport);
-
- // If encrypted file systems is enabled or disabled, this call will return the
- // correct directory.
- File baseStateDir = new File(Environment.getDataDirectory(), "backup");
-
- // This dir on /cache is managed directly in init.rc
- File dataDir = new File(Environment.getDownloadCacheDirectory(), "backup_stage");
-
- return new BackupManagerService(
- context,
- parent,
- backupThread,
- baseStateDir,
- dataDir,
- transportManager);
- }
-
- @VisibleForTesting
- public BackupManagerService(
- Context context,
- Trampoline parent,
- HandlerThread backupThread,
- File baseStateDir,
- File dataDir,
- TransportManager transportManager) {
- mContext = context;
- mPackageManager = context.getPackageManager();
- mPackageManagerBinder = AppGlobals.getPackageManager();
- mActivityManager = ActivityManager.getService();
-
- mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
-
- mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
-
- mAgentTimeoutParameters = new
- BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
- mAgentTimeoutParameters.start();
-
- // spin up the backup/restore handler thread
- mBackupHandler = new BackupHandler(this, backupThread.getLooper());
-
- // Set up our bookkeeping
- final ContentResolver resolver = context.getContentResolver();
- mProvisioned = Settings.Global.getInt(resolver,
- Settings.Global.DEVICE_PROVISIONED, 0) != 0;
- mAutoRestore = Settings.Secure.getInt(resolver,
- Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
-
- mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
- resolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
- false, mProvisionedObserver);
-
- mBaseStateDir = baseStateDir;
- mBaseStateDir.mkdirs();
- if (!SELinux.restorecon(mBaseStateDir)) {
- Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
- }
-
- mDataDir = dataDir;
-
- mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
-
- // Alarm receivers for scheduled backups & initialization operations
- mRunBackupReceiver = new RunBackupReceiver(this);
- IntentFilter filter = new IntentFilter();
- filter.addAction(RUN_BACKUP_ACTION);
- context.registerReceiver(mRunBackupReceiver, filter,
- android.Manifest.permission.BACKUP, null);
-
- mRunInitReceiver = new RunInitializeReceiver(this);
- filter = new IntentFilter();
- filter.addAction(RUN_INITIALIZE_ACTION);
- context.registerReceiver(mRunInitReceiver, filter,
- android.Manifest.permission.BACKUP, null);
-
- Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
- backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
-
- Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
- initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
-
- // Set up the backup-request journaling
- mJournalDir = new File(mBaseStateDir, "pending");
- mJournalDir.mkdirs(); // creates mBaseStateDir along the way
- mJournal = null; // will be created on first use
-
- mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
- // We are observing changes to the constants throughout the lifecycle of BMS. This is
- // because we reference the constants in multiple areas of BMS, which otherwise would
- // require frequent starting and stopping.
- mConstants.start();
-
- // Set up the various sorts of package tracking we do
- mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
- initPackageTracking();
-
- // Build our mapping of uid to backup client services. This implicitly
- // schedules a backup pass on the Package Manager metadata the first
- // time anything needs to be backed up.
- synchronized (mBackupParticipants) {
- addPackageParticipantsLocked(null);
- }
-
- mTransportManager = transportManager;
- mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
- mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
- mBackupHandler.postDelayed(
- mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
-
- // Now that we know about valid backup participants, parse any leftover journal files into
- // the pending backup set
- mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
-
- // Power management
- mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
- }
-
private void initPackageTracking() {
if (MORE_DEBUG) Slog.v(TAG, "` tracking");
@@ -2750,54 +2706,6 @@
}
}
- private static boolean backupSettingMigrated(int userId) {
- File base = new File(Environment.getDataDirectory(), "backup");
- File enableFile = new File(base, BACKUP_ENABLE_FILE);
- return enableFile.exists();
- }
-
- private static boolean readBackupEnableState(int userId) {
- File base = new File(Environment.getDataDirectory(), "backup");
- File enableFile = new File(base, BACKUP_ENABLE_FILE);
- if (enableFile.exists()) {
- try (FileInputStream fin = new FileInputStream(enableFile)) {
- int state = fin.read();
- return state != 0;
- } catch (IOException e) {
- // can't read the file; fall through to assume disabled
- Slog.e(TAG, "Cannot read enable state; assuming disabled");
- }
- } else {
- if (DEBUG) {
- Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
- }
- }
- return false;
- }
-
- private static void writeBackupEnableState(boolean enable, int userId) {
- File base = new File(Environment.getDataDirectory(), "backup");
- File enableFile = new File(base, BACKUP_ENABLE_FILE);
- File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
- try (FileOutputStream fout = new FileOutputStream(stage)) {
- fout.write(enable ? 1 : 0);
- fout.close();
- stage.renameTo(enableFile);
- // will be synced immediately by the try-with-resources call to close()
- } catch (IOException | RuntimeException e) {
- // Whoops; looks like we're doomed. Roll everything out, disabled,
- // including the legacy state.
- Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
- + e.getMessage());
-
- ContentResolver resolver = sInstance.getContext().getContentResolver();
- Settings.Secure.putStringForUser(resolver,
- Settings.Secure.BACKUP_ENABLED, null, userId);
- enableFile.delete();
- stage.delete();
- }
- }
-
// Enable/disable backups
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -3565,17 +3473,6 @@
pw.println(" " + s);
}
- if (DEBUG_BACKUP_TRACE) {
- synchronized (mBackupTrace) {
- if (!mBackupTrace.isEmpty()) {
- pw.println("Most recent backup trace:");
- for (String s : mBackupTrace) {
- pw.println(" " + s);
- }
- }
- }
- }
-
pw.print("Ancestral: ");
pw.println(Long.toHexString(mAncestralToken));
pw.print("Current: ");
@@ -3627,4 +3524,71 @@
return mBackupManagerBinder;
}
+ private static boolean backupSettingMigrated(int userId) {
+ File base = new File(Environment.getDataDirectory(), "backup");
+ File enableFile = new File(base, BACKUP_ENABLE_FILE);
+ return enableFile.exists();
+ }
+
+ private static boolean readBackupEnableState(int userId) {
+ File base = new File(Environment.getDataDirectory(), "backup");
+ File enableFile = new File(base, BACKUP_ENABLE_FILE);
+ if (enableFile.exists()) {
+ try (FileInputStream fin = new FileInputStream(enableFile)) {
+ int state = fin.read();
+ return state != 0;
+ } catch (IOException e) {
+ // can't read the file; fall through to assume disabled
+ Slog.e(TAG, "Cannot read enable state; assuming disabled");
+ }
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
+ }
+ }
+ return false;
+ }
+
+ private static void writeBackupEnableState(boolean enable, int userId) {
+ File base = new File(Environment.getDataDirectory(), "backup");
+ File enableFile = new File(base, BACKUP_ENABLE_FILE);
+ File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
+ try (FileOutputStream fout = new FileOutputStream(stage)) {
+ fout.write(enable ? 1 : 0);
+ fout.close();
+ stage.renameTo(enableFile);
+ // will be synced immediately by the try-with-resources call to close()
+ } catch (IOException | RuntimeException e) {
+ // Whoops; looks like we're doomed. Roll everything out, disabled,
+ // including the legacy state.
+ Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
+ + e.getMessage());
+
+ ContentResolver resolver = sInstance.getContext().getContentResolver();
+ Settings.Secure.putStringForUser(resolver,
+ Settings.Secure.BACKUP_ENABLED, null, userId);
+ enableFile.delete();
+ stage.delete();
+ }
+ }
+
+ /** Implementation to receive lifecycle event callbacks for system services. */
+ public static final class Lifecycle extends SystemService {
+ public Lifecycle(Context context) {
+ super(context);
+ sInstance = new Trampoline(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.BACKUP_SERVICE, sInstance);
+ }
+
+ @Override
+ public void onUnlockUser(int userId) {
+ if (userId == UserHandle.USER_SYSTEM) {
+ sInstance.unlockSystemUser();
+ }
+ }
+ }
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index e108026..755095e 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -45,9 +45,9 @@
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FullBackupJob;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.internal.Operation;
@@ -599,7 +599,6 @@
cleanUpPipes(enginePipes);
if (currentPackage.applicationInfo != null) {
Slog.i(TAG, "Unbinding agent in " + packageName);
- backupManagerService.addBackupTrace("unbinding " + packageName);
try {
backupManagerService.getActivityManager().unbindBackupAgent(
currentPackage.applicationInfo);
@@ -709,7 +708,6 @@
try {
backupManagerService.prepareOperationTimeout(
mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
- backupManagerService.addBackupTrace("preflighting");
if (MORE_DEBUG) {
Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 888ad1d..6174300 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -3,6 +3,7 @@
aidl: {
include_dirs: [
+ "frameworks/base/cmds/idmap2/idmap2d/aidl",
"frameworks/native/aidl/binder",
"frameworks/native/cmds/dumpstate/binder",
"system/core/storaged/binder",
@@ -13,6 +14,7 @@
srcs: [
"java/**/*.java",
":dumpstate_aidl",
+ ":idmap2_aidl",
":netd_aidl",
":netd_metrics_aidl",
":installd_aidl",
diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/AbstractMasterSystemService.java
index c955daf..6cae887 100644
--- a/services/core/java/com/android/server/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/AbstractMasterSystemService.java
@@ -244,7 +244,7 @@
*/
@GuardedBy("mLock")
@Nullable
- protected S peekServiceForUserLocked(int userId) {
+ protected S peekServiceForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
return mServicesCache.get(resolvedUserId);
@@ -254,7 +254,7 @@
* Updates a cached service for a given user.
*/
@GuardedBy("mLock")
- protected void updateCachedServiceLocked(int userId) {
+ protected void updateCachedServiceLocked(@UserIdInt int userId) {
updateCachedServiceLocked(userId, isDisabledLocked(userId));
}
@@ -262,7 +262,7 @@
* Checks whether the service is disabled (through {@link UserManager} restrictions) for the
* given user.
*/
- protected boolean isDisabledLocked(int userId) {
+ protected boolean isDisabledLocked(@UserIdInt int userId) {
return mDisabledUsers == null ? false : mDisabledUsers.get(userId);
}
@@ -274,7 +274,7 @@
* @return service for the user.
*/
@GuardedBy("mLock")
- protected S updateCachedServiceLocked(int userId, boolean disabled) {
+ protected S updateCachedServiceLocked(@UserIdInt int userId, boolean disabled) {
final S service = getServiceForUserLocked(userId);
if (service != null) {
service.updateLocked(disabled);
@@ -304,7 +304,7 @@
* <p>By default doesn't do anything, but can be overridden by subclasses.
*/
@SuppressWarnings("unused")
- protected void onServiceEnabledLocked(S service, @UserIdInt int userId) {
+ protected void onServiceEnabledLocked(@NonNull S service, @UserIdInt int userId) {
}
/**
@@ -314,15 +314,23 @@
*/
@GuardedBy("mLock")
@NonNull
- protected S removeCachedServiceLocked(@UserIdInt int userId) {
+ private S removeCachedServiceLocked(@UserIdInt int userId) {
final S service = peekServiceForUserLocked(userId);
if (service != null) {
mServicesCache.delete(userId);
+ onServiceRemoved(service, userId);
}
return service;
}
/**
+ * Called after the service is removed from the cache.
+ */
+ @SuppressWarnings("unused")
+ protected void onServiceRemoved(@NonNull S service, @UserIdInt int userId) {
+ }
+
+ /**
* Visits all services in the cache.
*/
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 5814064..fa98da5 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -80,6 +80,7 @@
import android.util.TimeUtils;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
@@ -219,6 +220,7 @@
SparseIntArray mProfileOwners;
+ @GuardedBy("this")
private CheckOpsDelegate mCheckOpsDelegate;
/**
@@ -1589,24 +1591,28 @@
public int checkOperation(int code, int uid, String packageName) {
final CheckOpsDelegate delegate;
synchronized (this) {
- if (mCheckOpsDelegate == null) {
- return checkOperationImpl(code, uid, packageName);
- }
delegate = mCheckOpsDelegate;
}
+ if (delegate == null) {
+ return checkOperationImpl(code, uid, packageName);
+ }
return delegate.checkOperation(code, uid, packageName,
AppOpsService.this::checkOperationImpl);
}
private int checkOperationImpl(int code, int uid, String packageName) {
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ return checkOperationUnchecked(code, uid, resolvedPackageName);
+ }
+
+ private int checkOperationUnchecked(int code, int uid, String packageName) {
synchronized (this) {
- verifyIncomingUid(uid);
- verifyIncomingOp(code);
- String resolvedPackageName = resolvePackageName(uid, packageName);
- if (resolvedPackageName == null) {
- return AppOpsManager.MODE_IGNORED;
- }
- if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
+ if (isOpRestrictedLocked(uid, code, packageName)) {
return AppOpsManager.MODE_IGNORED;
}
code = AppOpsManager.opToSwitch(code);
@@ -1615,7 +1621,7 @@
&& uidState.opModes.indexOfKey(code) >= 0) {
return uidState.opModes.get(code);
}
- Op op = getOpLocked(code, uid, resolvedPackageName, false, true, false);
+ Op op = getOpLocked(code, uid, packageName, false, true, false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
@@ -1627,31 +1633,31 @@
public int checkAudioOperation(int code, int usage, int uid, String packageName) {
final CheckOpsDelegate delegate;
synchronized (this) {
- if (mCheckOpsDelegate == null) {
- return checkAudioOperationImpl(code, usage, uid, packageName);
- }
delegate = mCheckOpsDelegate;
}
+ if (delegate == null) {
+ return checkAudioOperationImpl(code, usage, uid, packageName);
+ }
return delegate.checkAudioOperation(code, usage, uid, packageName,
AppOpsService.this::checkAudioOperationImpl);
}
private int checkAudioOperationImpl(int code, int usage, int uid, String packageName) {
+ boolean suspended;
+ try {
+ suspended = isPackageSuspendedForUser(packageName, uid);
+ } catch (IllegalArgumentException ex) {
+ // Package not found.
+ suspended = false;
+ }
+
+ if (suspended) {
+ Slog.i(TAG, "Audio disabled for suspended package=" + packageName
+ + " for uid=" + uid);
+ return AppOpsManager.MODE_IGNORED;
+ }
+
synchronized (this) {
- boolean suspended;
- try {
- suspended = isPackageSuspendedForUser(packageName, uid);
- } catch (IllegalArgumentException ex) {
- // Package not found.
- suspended = false;
- }
-
- if (suspended) {
- Slog.i(TAG, "Audio disabled for suspended package=" + packageName
- + " for uid=" + uid);
- return AppOpsManager.MODE_IGNORED;
- }
-
final int mode = checkRestrictionLocked(code, usage, uid, packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
return mode;
@@ -1754,11 +1760,11 @@
public int noteOperation(int code, int uid, String packageName) {
final CheckOpsDelegate delegate;
synchronized (this) {
- if (mCheckOpsDelegate == null) {
- return noteOperationImpl(code, uid, packageName);
- }
delegate = mCheckOpsDelegate;
}
+ if (delegate == null) {
+ return noteOperationImpl(code, uid, packageName);
+ }
return delegate.noteOperation(code, uid, packageName,
AppOpsService.this::noteOperationImpl);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b5217ad..c660cc6 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -677,6 +677,8 @@
stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
}
r.callStart = false;
+ StatsLog.write(StatsLog.SERVICE_STATE_CHANGED, r.appInfo.uid, r.name.getPackageName(),
+ r.name.getClassName(), StatsLog.SERVICE_STATE_CHANGED__STATE__START);
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
}
@@ -715,6 +717,9 @@
service.delayedStop = true;
return;
}
+ StatsLog.write(StatsLog.SERVICE_STATE_CHANGED, service.appInfo.uid,
+ service.name.getPackageName(), service.name.getClassName(),
+ StatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
synchronized (service.stats.getBatteryStats()) {
service.stats.stopRunningLocked();
}
@@ -856,6 +861,8 @@
}
}
+ StatsLog.write(StatsLog.SERVICE_STATE_CHANGED, r.appInfo.uid, r.name.getPackageName(),
+ r.name.getClassName(), StatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
synchronized (r.stats.getBatteryStats()) {
r.stats.stopRunningLocked();
}
@@ -2517,6 +2524,8 @@
EventLogTags.writeAmCreateService(
r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
}
+ StatsLog.write(StatsLog.SERVICE_LAUNCH_REPORTED, r.appInfo.uid, r.name.getPackageName(),
+ r.name.getClassName());
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 771d376..aa96082 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4820,6 +4820,10 @@
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
int flags, Bundle bOptions, int userId) {
+
+ // NOTE: The service lock isn't held in this method because nothing in the method requires
+ // the service lock to be held.
+
enforceNotIsolatedCaller("getIntentSender");
// Refuse possible leaked file descriptors
if (intents != null) {
@@ -4851,43 +4855,41 @@
}
}
- synchronized(this) {
- int callingUid = Binder.getCallingUid();
- int origUserId = userId;
- userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
- type == ActivityManager.INTENT_SENDER_BROADCAST,
- ALLOW_NON_FULL, "getIntentSender", null);
- if (origUserId == UserHandle.USER_CURRENT) {
- // We don't want to evaluate this until the pending intent is
- // actually executed. However, we do want to always do the
- // security checking for it above.
- userId = UserHandle.USER_CURRENT;
- }
- try {
- if (callingUid != 0 && callingUid != SYSTEM_UID) {
- final int uid = AppGlobals.getPackageManager().getPackageUid(packageName,
- MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid));
- if (!UserHandle.isSameApp(callingUid, uid)) {
- String msg = "Permission Denial: getIntentSender() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + ", (need uid=" + uid + ")"
- + " is not allowed to send as package " + packageName;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ int callingUid = Binder.getCallingUid();
+ int origUserId = userId;
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
+ type == ActivityManager.INTENT_SENDER_BROADCAST,
+ ALLOW_NON_FULL, "getIntentSender", null);
+ if (origUserId == UserHandle.USER_CURRENT) {
+ // We don't want to evaluate this until the pending intent is
+ // actually executed. However, we do want to always do the
+ // security checking for it above.
+ userId = UserHandle.USER_CURRENT;
+ }
+ try {
+ if (callingUid != 0 && callingUid != SYSTEM_UID) {
+ final int uid = AppGlobals.getPackageManager().getPackageUid(packageName,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid));
+ if (!UserHandle.isSameApp(callingUid, uid)) {
+ String msg = "Permission Denial: getIntentSender() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + ", (need uid=" + uid + ")"
+ + " is not allowed to send as package " + packageName;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
+ }
- if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
- return mAtmInternal.getIntentSender(type, packageName, callingUid, userId,
- token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
- }
- return mPendingIntentController.getIntentSender(type, packageName, callingUid,
- userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
- bOptions);
- } catch (RemoteException e) {
- throw new SecurityException(e);
+ if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+ return mAtmInternal.getIntentSender(type, packageName, callingUid, userId,
+ token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
}
+ return mPendingIntentController.getIntentSender(type, packageName, callingUid,
+ userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+ bOptions);
+ } catch (RemoteException e) {
+ throw new SecurityException(e);
}
}
@@ -7002,7 +7004,7 @@
mCoreSettingsObserver = new CoreSettingsObserver(this);
mActivityTaskManager.installSystemProviders();
mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();
- GlobalSettingsToPropertiesMapper.start(mContext.getContentResolver());
+ SettingsToPropertiesMapper.start(mContext.getContentResolver());
// Now that the settings provider is published we can consider sending
// in a rescue party.
diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
deleted file mode 100644
index 1366c21..0000000
--- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.am;
-
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Build;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.view.ThreadedRenderer;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-
-/**
- * Maps global system settings to system properties.
- * <p>The properties are dynamically updated when settings change.
- */
-class GlobalSettingsToPropertiesMapper {
-
- private static final String TAG = "GlobalSettingsToPropertiesMapper";
-
- // List mapping entries in the following format:
- // {Settings.Global.SETTING_NAME, "system_property_name"}
- // Important: Property being added should be whitelisted by SELinux policy or have one of the
- // already whitelisted prefixes in system_server.te, e.g. sys.
- private static final String[][] sGlobalSettingsMapping = new String[][] {
- {Settings.Global.SYS_VDSO, "sys.vdso"},
- {Settings.Global.FPS_DEVISOR, ThreadedRenderer.DEBUG_FPS_DIVISOR},
- {Settings.Global.DISPLAY_PANEL_LPM, "sys.display_panel_lpm"},
- {Settings.Global.SYS_UIDCPUPOWER, "sys.uidcpupower"},
- {Settings.Global.SYS_TRACED, "sys.traced.enable_override"},
- };
-
-
- private final ContentResolver mContentResolver;
- private final String[][] mGlobalSettingsMapping;
-
- @VisibleForTesting
- GlobalSettingsToPropertiesMapper(ContentResolver contentResolver,
- String[][] globalSettingsMapping) {
- mContentResolver = contentResolver;
- mGlobalSettingsMapping = globalSettingsMapping;
- }
-
- void updatePropertiesFromGlobalSettings() {
- for (String[] entry : mGlobalSettingsMapping) {
- final String settingName = entry[0];
- final String propName = entry[1];
- Uri settingUri = Settings.Global.getUriFor(settingName);
- Preconditions.checkNotNull(settingUri, "Setting " + settingName + " not found");
- ContentObserver co = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- updatePropertyFromSetting(settingName, propName);
- }
- };
- updatePropertyFromSetting(settingName, propName);
- mContentResolver.registerContentObserver(settingUri, false, co);
- }
- }
-
- public static void start(ContentResolver contentResolver) {
- new GlobalSettingsToPropertiesMapper(contentResolver, sGlobalSettingsMapping)
- .updatePropertiesFromGlobalSettings();
- }
-
- private String getGlobalSetting(String name) {
- return Settings.Global.getString(mContentResolver, name);
- }
-
- private void setProperty(String key, String value) {
- // Check if need to clear the property
- if (value == null) {
- // It's impossible to remove system property, therefore we check previous value to
- // avoid setting an empty string if the property wasn't set.
- if (TextUtils.isEmpty(systemPropertiesGet(key))) {
- return;
- }
- value = "";
- }
- try {
- systemPropertiesSet(key, value);
- } catch (Exception e) {
- // Failure to set a property can be caused by SELinux denial. This usually indicates
- // that the property wasn't whitelisted in sepolicy.
- // No need to report it on all user devices, only on debug builds.
- if (Build.IS_DEBUGGABLE) {
- Slog.wtf(TAG, "Unable to set property " + key + " value '" + value + "'", e);
- } else {
- Slog.e(TAG, "Unable to set property " + key + " value '" + value + "'", e);
- }
- }
- }
-
- @VisibleForTesting
- protected String systemPropertiesGet(String key) {
- return SystemProperties.get(key);
- }
-
- @VisibleForTesting
- protected void systemPropertiesSet(String key, String value) {
- SystemProperties.set(key, value);
- }
-
- @VisibleForTesting
- void updatePropertyFromSetting(String settingName, String propName) {
- String settingValue = getGlobalSetting(settingName);
- setProperty(propName, settingValue);
- }
-}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 79c98e5..5208ca5 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -27,4 +27,4 @@
michaelwr@google.com
narayan@google.com
-per-file GlobalSettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
new file mode 100644
index 0000000..a5848ca
--- /dev/null
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashSet;
+
+/**
+ * Maps system settings to system properties.
+ * <p>The properties are dynamically updated when settings change.
+ */
+class SettingsToPropertiesMapper {
+
+ private static final String TAG = "SettingsToPropertiesMapper";
+
+ private static final String SYSTEM_PROPERTY_PREFIX = "persist.device_config.";
+
+ private static final String RESET_PERFORMED_PROPERTY = "device_config.reset_performed";
+
+ private static final String RESET_RECORD_FILE_PATH =
+ "/data/server_configurable_flags/reset_flags";
+
+ private static final String SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$";
+
+ private static final String SYSTEM_PROPERTY_INVALID_SUBSTRING = "..";
+
+ private static final int SYSTEM_PROPERTY_MAX_LENGTH = 92;
+
+ // experiment flags added to Global.Settings(before new "Config" provider table is available)
+ // will be added under this category.
+ private static final String GLOBAL_SETTINGS_CATEGORY = "global_settings";
+
+ // Add the global setting you want to push to native level as experiment flag into this list.
+ //
+ // NOTE: please grant write permission system property prefix
+ // with format persist.experiment.[experiment_category_name]. in system_server.te and grant read
+ // permission in the corresponding .te file your feature belongs to.
+ @VisibleForTesting
+ static final String[] sGlobalSettings = new String[] {
+ };
+
+ @VisibleForTesting
+ static final String[] sDeviceConfigScopes = new String[] {
+ };
+
+ private final String[] mGlobalSettings;
+
+ private final String[] mDeviceConfigScopes;
+
+ private final ContentResolver mContentResolver;
+
+ @VisibleForTesting
+ protected SettingsToPropertiesMapper(ContentResolver contentResolver,
+ String[] globalSettings,
+ String[] deviceConfigScopes) {
+ mContentResolver = contentResolver;
+ mGlobalSettings = globalSettings;
+ mDeviceConfigScopes = deviceConfigScopes;
+ }
+
+ @VisibleForTesting
+ void updatePropertiesFromSettings() {
+ for (String globalSetting : mGlobalSettings) {
+ Uri settingUri = Settings.Global.getUriFor(globalSetting);
+ String propName = makePropertyName(GLOBAL_SETTINGS_CATEGORY, globalSetting);
+ if (settingUri == null) {
+ log("setting uri is null for globalSetting " + globalSetting);
+ continue;
+ }
+ if (propName == null) {
+ log("invalid prop name for globalSetting " + globalSetting);
+ continue;
+ }
+
+ ContentObserver co = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updatePropertyFromSetting(globalSetting, propName, true);
+ }
+ };
+
+ // only updating on starting up when no native flags reset is performed during current
+ // booting.
+ if (!isNativeFlagsResetPerformed()) {
+ updatePropertyFromSetting(globalSetting, propName, true);
+ }
+ mContentResolver.registerContentObserver(settingUri, false, co);
+ }
+
+ // TODO: address sDeviceConfigScopes after DeviceConfig APIs are available.
+ }
+
+ public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
+ SettingsToPropertiesMapper mapper = new SettingsToPropertiesMapper(
+ contentResolver, sGlobalSettings, sDeviceConfigScopes);
+ mapper.updatePropertiesFromSettings();
+ return mapper;
+ }
+
+ /**
+ * If native level flags reset has been performed as an attempt to recover from a crash loop
+ * during current device booting.
+ * @return
+ */
+ public boolean isNativeFlagsResetPerformed() {
+ String value = systemPropertiesGet(RESET_PERFORMED_PROPERTY);
+ return "true".equals(value);
+ }
+
+ /**
+ * return an array of native flag categories under which flags got reset during current device
+ * booting.
+ * @return
+ */
+ public String[] getResetNativeCategories() {
+ if (!isNativeFlagsResetPerformed()) {
+ return new String[0];
+ }
+
+ String content = getResetFlagsFileContent();
+ if (TextUtils.isEmpty(content)) {
+ return new String[0];
+ }
+
+ String[] property_names = content.split(";");
+ HashSet<String> categories = new HashSet<>();
+ for (String property_name : property_names) {
+ String[] segments = property_name.split("\\.");
+ if (segments.length < 3) {
+ log("failed to extract category name from property " + property_name);
+ continue;
+ }
+ categories.add(segments[2]);
+ }
+ return categories.toArray(new String[0]);
+ }
+
+ /**
+ * system property name constructing rule: "persist.device_config.[category_name].[flag_name]".
+ * If the name contains invalid characters or substrings for system property name,
+ * will return null.
+ * @param categoryName
+ * @param flagName
+ * @return
+ */
+ @VisibleForTesting
+ static String makePropertyName(String categoryName, String flagName) {
+ String propertyName = SYSTEM_PROPERTY_PREFIX + categoryName + "." + flagName;
+
+ if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX)
+ || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) {
+ return null;
+ }
+
+ return propertyName;
+ }
+
+ private String getSetting(String name, boolean isGlobalSetting) {
+ if (isGlobalSetting) {
+ return Settings.Global.getString(mContentResolver, name);
+ } else {
+ // TODO: complete the code after DeviceConfig APIs implemented.
+ return null;
+ }
+ }
+
+ private void setProperty(String key, String value) {
+ // Check if need to clear the property
+ if (value == null) {
+ // It's impossible to remove system property, therefore we check previous value to
+ // avoid setting an empty string if the property wasn't set.
+ if (TextUtils.isEmpty(systemPropertiesGet(key))) {
+ return;
+ }
+ value = "";
+ } else if (value.length() > SYSTEM_PROPERTY_MAX_LENGTH) {
+ log(value + " exceeds system property max length.");
+ return;
+ }
+
+ try {
+ systemPropertiesSet(key, value);
+ } catch (Exception e) {
+ // Failure to set a property can be caused by SELinux denial. This usually indicates
+ // that the property wasn't whitelisted in sepolicy.
+ // No need to report it on all user devices, only on debug builds.
+ log("Unable to set property " + key + " value '" + value + "'", e);
+ }
+ }
+
+ private static void log(String msg, Exception e) {
+ if (Build.IS_DEBUGGABLE) {
+ Slog.wtf(TAG, msg, e);
+ } else {
+ Slog.e(TAG, msg, e);
+ }
+ }
+
+ private static void log(String msg) {
+ if (Build.IS_DEBUGGABLE) {
+ Slog.wtf(TAG, msg);
+ } else {
+ Slog.e(TAG, msg);
+ }
+ }
+
+ @VisibleForTesting
+ protected String systemPropertiesGet(String key) {
+ return SystemProperties.get(key);
+ }
+
+ @VisibleForTesting
+ protected void systemPropertiesSet(String key, String value) {
+ SystemProperties.set(key, value);
+ }
+
+ @VisibleForTesting
+ protected String getResetFlagsFileContent() {
+ String content = null;
+ try {
+ File reset_flag_file = new File(RESET_RECORD_FILE_PATH);
+ BufferedReader br = new BufferedReader(new FileReader(reset_flag_file));
+ content = br.readLine();
+
+ br.close();
+ } catch (IOException ioe) {
+ log("failed to read file " + RESET_RECORD_FILE_PATH, ioe);
+ }
+ return content;
+ }
+
+ @VisibleForTesting
+ void updatePropertyFromSetting(String settingName, String propName, boolean isGlobalSetting) {
+ String settingValue = getSetting(settingName, isGlobalSetting);
+ setProperty(propName, settingValue);
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index c20079e..3a31c9c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -72,6 +72,8 @@
import android.view.IInputFilterHost;
import android.view.IWindow;
import android.view.InputChannel;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.KeyEvent;
@@ -197,7 +199,7 @@
private static native boolean nativeHasKeys(long ptr,
int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
- InputWindowHandle inputWindowHandle, int displayId);
+ int displayId);
private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
private static native int nativeInjectInputEvent(long ptr, InputEvent event,
@@ -486,8 +488,7 @@
}
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
- // Register channel for monitor.
- nativeRegisterInputChannel(mPtr, inputChannels[0], null, displayId);
+ nativeRegisterInputChannel(mPtr, inputChannels[0], displayId);
inputChannels[0].dispose(); // don't need to retain the Java object reference
return inputChannels[1];
}
@@ -498,14 +499,17 @@
* @param inputWindowHandle The handle of the input window associated with the
* input channel, or null if none.
*/
- public void registerInputChannel(InputChannel inputChannel,
- InputWindowHandle inputWindowHandle) {
+ public void registerInputChannel(InputChannel inputChannel, IBinder token) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
- // Register channel for normal.
- nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, Display.INVALID_DISPLAY);
+ if (token == null) {
+ token = new Binder();
+ }
+ inputChannel.setToken(token);
+
+ nativeRegisterInputChannel(mPtr, inputChannel, Display.INVALID_DISPLAY);
}
/**
@@ -1791,15 +1795,15 @@
}
// Native callback.
- private void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
- mWindowManagerCallbacks.notifyInputChannelBroken(inputWindowHandle);
+ private void notifyInputChannelBroken(IBinder token) {
+ mWindowManagerCallbacks.notifyInputChannelBroken(token);
}
// Native callback.
private long notifyANR(InputApplicationHandle inputApplicationHandle,
- InputWindowHandle inputWindowHandle, String reason) {
+ IBinder token, String reason) {
return mWindowManagerCallbacks.notifyANR(
- inputApplicationHandle, inputWindowHandle, reason);
+ inputApplicationHandle, token, reason);
}
// Native callback.
@@ -1830,13 +1834,13 @@
}
// Native callback.
- private long interceptKeyBeforeDispatching(InputWindowHandle focus,
+ private long interceptKeyBeforeDispatching(IBinder focus,
KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
// Native callback.
- private KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
+ private KeyEvent dispatchUnhandledKey(IBinder focus,
KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
}
@@ -1987,19 +1991,19 @@
public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);
- public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);
+ public void notifyInputChannelBroken(IBinder token);
public long notifyANR(InputApplicationHandle inputApplicationHandle,
- InputWindowHandle inputWindowHandle, String reason);
+ IBinder token, String reason);
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags);
- public long interceptKeyBeforeDispatching(InputWindowHandle focus,
+ public long interceptKeyBeforeDispatching(IBinder token,
KeyEvent event, int policyFlags);
- public KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
+ public KeyEvent dispatchUnhandledKey(IBinder token,
KeyEvent event, int policyFlags);
public int getPointerLayer();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1c7572e..6195ed9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -870,6 +870,7 @@
}
if (expanded && userAction) {
r.recordExpanded();
+ reportUserInteraction(r);
}
EventLogTags.writeNotificationExpansion(key,
userAction ? 1 : 0, expanded ? 1 : 0,
@@ -885,6 +886,9 @@
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
r.recordDirectReplied();
+ mMetricsLogger.write(r.getLogMaker()
+ .setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
+ .setType(MetricsEvent.TYPE_ACTION));
reportUserInteraction(r);
}
}
@@ -1160,6 +1164,7 @@
mConditionProviders.onUserSwitched(userId);
mListeners.onUserSwitched(userId);
mZenModeHelper.onUserSwitched(userId);
+ mPreferencesHelper.onUserSwitched(userId);
}
// assistant is the only thing that cares about managed profiles specifically
mAssistants.onUserSwitched(userId);
@@ -1188,6 +1193,7 @@
mConditionProviders.onUserUnlocked(userId);
mListeners.onUserUnlocked(userId);
mZenModeHelper.onUserUnlocked(userId);
+ mPreferencesHelper.onUserUnlocked(userId);
}
}
}
@@ -1982,7 +1988,7 @@
}
/**
- * Report to usage stats that the notification was clicked.
+ * Report to usage stats that the user interacted with the notification.
* @param r notification record
*/
protected void reportUserInteraction(NotificationRecord r) {
@@ -2525,6 +2531,19 @@
}
@Override
+ public int getAppsBypassingDndCount(int userId) {
+ checkCallerIsSystem();
+ return mPreferencesHelper.getAppsBypassingDndCount(userId);
+ }
+
+ @Override
+ public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(
+ String pkg, int userId) {
+ checkCallerIsSystem();
+ return mPreferencesHelper.getNotificationChannelsBypassingDnd(pkg, userId);
+ }
+
+ @Override
public boolean areChannelsBypassingDnd() {
return mPreferencesHelper.areChannelsBypassingDnd();
}
@@ -4521,6 +4540,7 @@
mDuration)
.addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
mSnoozeCriterionId == null ? 0 : 1));
+ reportUserInteraction(r);
boolean wasPosted = removeFromNotificationListsLocked(r);
cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
updateLightsLocked();
@@ -5529,7 +5549,7 @@
ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
- ArrayList<ArrayList<Notification.Action>> smartActionsBefore = new ArrayList<>(N);
+ ArrayList<ArrayList<Notification.Action>> systemSmartActionsBefore = new ArrayList<>(N);
ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
@@ -5542,7 +5562,7 @@
snoozeCriteriaBefore.add(r.getSnoozeCriteria());
userSentimentBefore.add(r.getUserSentiment());
suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
- smartActionsBefore.add(r.getSmartActions());
+ systemSmartActionsBefore.add(r.getSystemGeneratedSmartActions());
smartRepliesBefore.add(r.getSmartReplies());
mRankingHelper.extractSignals(r);
}
@@ -5559,7 +5579,8 @@
|| !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())
|| !Objects.equals(suppressVisuallyBefore.get(i),
r.getSuppressedVisualEffects())
- || !Objects.equals(smartActionsBefore.get(i), r.getSmartActions())
+ || !Objects.equals(systemSmartActionsBefore.get(i),
+ r.getSystemGeneratedSmartActions())
|| !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())) {
mHandler.scheduleSendRankingUpdate();
return;
@@ -6561,7 +6582,7 @@
Bundle showBadge = new Bundle();
Bundle userSentiment = new Bundle();
Bundle hidden = new Bundle();
- Bundle smartActions = new Bundle();
+ Bundle systemGeneratedSmartActions = new Bundle();
Bundle smartReplies = new Bundle();
Bundle audiblyAlerted = new Bundle();
Bundle noisy = new Bundle();
@@ -6592,7 +6613,8 @@
showBadge.putBoolean(key, record.canShowBadge());
userSentiment.putInt(key, record.getUserSentiment());
hidden.putBoolean(key, record.isHidden());
- smartActions.putParcelableArrayList(key, record.getSmartActions());
+ systemGeneratedSmartActions.putParcelableArrayList(key,
+ record.getSystemGeneratedSmartActions());
smartReplies.putCharSequenceArrayList(key, record.getSmartReplies());
audiblyAlerted.putBoolean(key, record.getAudiblyAlerted());
noisy.putBoolean(key, record.getSound() != null || record.getVibration() != null);
@@ -6607,7 +6629,7 @@
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden,
- smartActions, smartReplies, audiblyAlerted, noisy);
+ systemGeneratedSmartActions, smartReplies, audiblyAlerted, noisy);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index a11b03f..1a9257c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -163,7 +163,11 @@
private Light mLight;
private String mGroupLogTag;
private String mChannelIdLogTag;
- private ArrayList<Notification.Action> mSmartActions;
+ /**
+ * This list contains system generated smart actions from NAS, app-generated smart actions are
+ * stored in Notification.actions marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION.
+ */
+ private ArrayList<Notification.Action> mSystemGeneratedSmartActions;
private ArrayList<CharSequence> mSmartReplies;
private final List<Adjustment> mAdjustments;
@@ -653,10 +657,11 @@
}
}
if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
- setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
+ setSystemGeneratedSmartActions(
+ signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
MetricsLogger.action(getAdjustmentLogMaker()
.addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_ACTIONS,
- getSmartActions().size()));
+ getSystemGeneratedSmartActions().size()));
}
if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) {
setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES));
@@ -1132,12 +1137,13 @@
mHasSeenSmartReplies = hasSeenSmartReplies;
}
- public void setSmartActions(ArrayList<Notification.Action> smartActions) {
- mSmartActions = smartActions;
+ public void setSystemGeneratedSmartActions(
+ ArrayList<Notification.Action> systemGeneratedSmartActions) {
+ mSystemGeneratedSmartActions = systemGeneratedSmartActions;
}
- public ArrayList<Notification.Action> getSmartActions() {
- return mSmartActions;
+ public ArrayList<Notification.Action> getSystemGeneratedSmartActions() {
+ return mSystemGeneratedSmartActions;
}
public void setSmartReplies(ArrayList<CharSequence> smartReplies) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 8fce5e3..fd65ebe 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -111,7 +111,6 @@
// pkg => PackagePreferences
private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
-
private final Context mContext;
private final PackageManager mPm;
private final RankingHandler mRankingHandler;
@@ -120,7 +119,6 @@
private SparseBooleanArray mBadgingEnabled;
private boolean mAreChannelsBypassingDnd;
-
public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
ZenModeHelper zenHelper) {
mContext = context;
@@ -129,11 +127,7 @@
mPm = pm;
updateBadgingEnabled();
-
- mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state &
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
- updateChannelsBypassingDnd();
-
+ syncChannelsBypassingDnd(mContext.getUserId());
}
public void readXml(XmlPullParser parser, boolean forRestore)
@@ -525,6 +519,7 @@
// but the system can
if (group.isBlocked() != oldGroup.isBlocked()) {
group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
+ updateChannelsBypassingDnd(mContext.getUserId());
}
if (group.canOverlayApps() != oldGroup.canOverlayApps()) {
group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY);
@@ -571,6 +566,7 @@
// Apps are allowed to downgrade channel importance if the user has not changed any
// fields on this channel yet.
+ final int previousExistingImportance = existing.getImportance();
if (existing.getUserLockedFields() == 0 &&
channel.getImportance() < existing.getImportance()) {
existing.setImportance(channel.getImportance());
@@ -582,8 +578,9 @@
boolean bypassDnd = channel.canBypassDnd();
existing.setBypassDnd(bypassDnd);
- if (bypassDnd != mAreChannelsBypassingDnd) {
- updateChannelsBypassingDnd();
+ if (bypassDnd != mAreChannelsBypassingDnd
+ || previousExistingImportance != existing.getImportance()) {
+ updateChannelsBypassingDnd(mContext.getUserId());
}
}
@@ -613,7 +610,7 @@
r.channels.put(channel.getId(), channel);
if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
- updateChannelsBypassingDnd();
+ updateChannelsBypassingDnd(mContext.getUserId());
}
MetricsLogger.action(getChannelLog(channel, pkg).setType(
com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
@@ -663,8 +660,9 @@
MetricsLogger.action(getChannelLog(updatedChannel, pkg));
}
- if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) {
- updateChannelsBypassingDnd();
+ if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
+ || channel.getImportance() != updatedChannel.getImportance()) {
+ updateChannelsBypassingDnd(mContext.getUserId());
}
updateConfig();
}
@@ -701,7 +699,7 @@
MetricsLogger.action(lm);
if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
- updateChannelsBypassingDnd();
+ updateChannelsBypassingDnd(mContext.getUserId());
}
}
}
@@ -859,6 +857,27 @@
}
/**
+ * Gets all notification channels associated with the given pkg and userId that can bypass dnd
+ */
+ public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
+ int userId) {
+ List<NotificationChannel> channels = new ArrayList<>();
+ synchronized (mPackagePreferences) {
+ final PackagePreferences r = mPackagePreferences.get(
+ packagePreferencesKey(pkg, userId));
+ // notifications from this package aren't blocked
+ if (r != null && r.importance != IMPORTANCE_NONE) {
+ for (NotificationChannel channel : r.channels.values()) {
+ if (channelIsLive(r, channel) && channel.canBypassDnd()) {
+ channels.add(channel);
+ }
+ }
+ }
+ }
+ return new ParceledListSlice<>(channels);
+ }
+
+ /**
* True for pre-O apps that only have the default channel, or pre O apps that have no
* channels yet. This method will create the default channel for pre-O apps that don't have it.
* Should never be true for O+ targeting apps, but that's enforced on boot/when an app
@@ -922,18 +941,62 @@
return count;
}
- public void updateChannelsBypassingDnd() {
+ /**
+ * Returns the number of apps that have at least one notification channel that can bypass DND
+ * for given particular user
+ */
+ public int getAppsBypassingDndCount(int userId) {
+ int count = 0;
synchronized (mPackagePreferences) {
- final int numPackagePreferencess = mPackagePreferences.size();
- for (int PackagePreferencesIndex = 0; PackagePreferencesIndex < numPackagePreferencess;
- PackagePreferencesIndex++) {
- final PackagePreferences r = mPackagePreferences.valueAt(PackagePreferencesIndex);
- final int numChannels = r.channels.size();
+ final int numPackagePreferences = mPackagePreferences.size();
+ for (int i = 0; i < numPackagePreferences; i++) {
+ final PackagePreferences r = mPackagePreferences.valueAt(i);
+ // Package isn't associated with this userId or notifications from this package are
+ // blocked
+ if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
+ continue;
+ }
- for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) {
- NotificationChannel channel = r.channels.valueAt(channelIndex);
- if (!channel.isDeleted() && channel.canBypassDnd()) {
- // If any channel bypasses DND, synchronize state and return early.
+ for (NotificationChannel channel : r.channels.values()) {
+ if (channelIsLive(r, channel) && channel.canBypassDnd()) {
+ count++;
+ break;
+ }
+ }
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
+ * updating
+ * @param userId
+ */
+ private void syncChannelsBypassingDnd(int userId) {
+ mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
+ & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
+ updateChannelsBypassingDnd(userId);
+ }
+
+ /**
+ * Updates the user's NotificationPolicy based on whether the given userId
+ * has channels bypassing DND
+ * @param userId
+ */
+ private void updateChannelsBypassingDnd(int userId) {
+ synchronized (mPackagePreferences) {
+ final int numPackagePreferences = mPackagePreferences.size();
+ for (int i = 0; i < numPackagePreferences; i++) {
+ final PackagePreferences r = mPackagePreferences.valueAt(i);
+ // Package isn't associated with this userId or notifications from this package are
+ // blocked
+ if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
+ continue;
+ }
+
+ for (NotificationChannel channel : r.channels.values()) {
+ if (channelIsLive(r, channel) && channel.canBypassDnd()) {
if (!mAreChannelsBypassingDnd) {
mAreChannelsBypassingDnd = true;
updateZenPolicy(true);
@@ -943,7 +1006,6 @@
}
}
}
-
// If no channels bypass DND, update the zen policy once to disable DND bypass.
if (mAreChannelsBypassingDnd) {
mAreChannelsBypassingDnd = false;
@@ -951,6 +1013,22 @@
}
}
+ private boolean channelIsLive(PackagePreferences pkgPref, NotificationChannel channel) {
+ // Channel is in a group that's blocked
+ if (!TextUtils.isEmpty(channel.getGroup())) {
+ if (pkgPref.groups.get(channel.getGroup()).isBlocked()) {
+ return false;
+ }
+ }
+
+ // Channel is deleted or is blocked
+ if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
+ return false;
+ }
+
+ return true;
+ }
+
public void updateZenPolicy(boolean areChannelsBypassingDnd) {
NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
@@ -1329,6 +1407,20 @@
return packageChannels;
}
+ /**
+ * Called when user switches
+ */
+ public void onUserSwitched(int userId) {
+ syncChannelsBypassingDnd(userId);
+ }
+
+ /**
+ * Called when user is unlocked
+ */
+ public void onUserUnlocked(int userId) {
+ syncChannelsBypassingDnd(userId);
+ }
+
public void onUserRemoved(int userId) {
synchronized (mPackagePreferences) {
int N = mPackagePreferences.size();
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 807c343..731e6bc 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -16,36 +16,46 @@
package com.android.server.om;
+import static android.content.Context.IDMAP_SERVICE;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
import static com.android.server.om.OverlayManagerService.DEBUG;
import static com.android.server.om.OverlayManagerService.TAG;
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
import android.content.pm.PackageInfo;
+import android.os.IBinder;
+import android.os.IIdmap2;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Slog;
-import com.android.server.pm.Installer.InstallerException;
+import com.android.internal.os.BackgroundThread;
import com.android.server.pm.Installer;
-import java.io.DataInputStream;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
/**
* Handle the creation and deletion of idmap files.
*
* The actual work is performed by the idmap binary, launched through Installer
- * and installd.
+ * and installd (or idmap2).
*
* Note: this class is subclassed in the OMS unit tests, and hence not marked as final.
*/
class IdmapManager {
+ private static final boolean FEATURE_FLAG_IDMAP2 = false;
+
private final Installer mInstaller;
+ private IIdmap2 mIdmap2Service;
IdmapManager(final Installer installer) {
mInstaller = installer;
+ if (FEATURE_FLAG_IDMAP2) {
+ connectToIdmap2d();
+ }
}
boolean createIdmap(@NonNull final PackageInfo targetPackage,
@@ -59,8 +69,12 @@
final String targetPath = targetPackage.applicationInfo.getBaseCodePath();
final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
try {
- mInstaller.idmap(targetPath, overlayPath, sharedGid);
- } catch (InstallerException e) {
+ if (FEATURE_FLAG_IDMAP2) {
+ mIdmap2Service.createIdmap(targetPath, overlayPath, userId);
+ } else {
+ mInstaller.idmap(targetPath, overlayPath, sharedGid);
+ }
+ } catch (Exception e) {
Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
+ overlayPath + ": " + e.getMessage());
return false;
@@ -69,13 +83,16 @@
}
boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
- // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
if (DEBUG) {
Slog.d(TAG, "remove idmap for " + oi.baseCodePath);
}
try {
- mInstaller.removeIdmap(oi.baseCodePath);
- } catch (InstallerException e) {
+ if (FEATURE_FLAG_IDMAP2) {
+ mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
+ } else {
+ mInstaller.removeIdmap(oi.baseCodePath);
+ }
+ } catch (Exception e) {
Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
return false;
}
@@ -83,19 +100,58 @@
}
boolean idmapExists(@NonNull final OverlayInfo oi) {
- // unused OverlayInfo.userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
- return new File(getIdmapPath(oi.baseCodePath)).isFile();
+ return new File(getIdmapPath(oi.baseCodePath, oi.userId)).isFile();
}
boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
- // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
- return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())).isFile();
+ return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath(), userId))
+ .isFile();
}
- private String getIdmapPath(@NonNull final String baseCodePath) {
- final StringBuilder sb = new StringBuilder("/data/resource-cache/");
- sb.append(baseCodePath.substring(1).replace('/', '@'));
- sb.append("@idmap");
- return sb.toString();
+ private @NonNull String getIdmapPath(@NonNull final String overlayPackagePath,
+ final int userId) {
+ if (FEATURE_FLAG_IDMAP2) {
+ try {
+ return mIdmap2Service.getIdmapPath(overlayPackagePath, userId);
+ } catch (Exception e) {
+ Slog.w(TAG, "failed to get idmap path for " + overlayPackagePath + ": "
+ + e.getMessage());
+ return "";
+ }
+ } else {
+ final StringBuilder sb = new StringBuilder("/data/resource-cache/");
+ sb.append(overlayPackagePath.substring(1).replace('/', '@'));
+ sb.append("@idmap");
+ return sb.toString();
+ }
+ }
+
+ private void connectToIdmap2d() {
+ IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
+ if (binder != null) {
+ try {
+ binder.linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Slog.w(TAG, "service '" + IDMAP_SERVICE + "' died; reconnecting...");
+ connectToIdmap2d();
+ }
+
+ }, 0);
+ } catch (RemoteException e) {
+ binder = null;
+ }
+ }
+ if (binder != null) {
+ mIdmap2Service = IIdmap2.Stub.asInterface(binder);
+ if (DEBUG) {
+ Slog.d(TAG, "service '" + IDMAP_SERVICE + "' connected");
+ }
+ } else {
+ Slog.w(TAG, "service '" + IDMAP_SERVICE + "' not found; trying again...");
+ BackgroundThread.getHandler().postDelayed(() -> {
+ connectToIdmap2d();
+ }, SECOND_IN_MILLIS);
+ }
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 36bf83d..572d368 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -179,19 +179,13 @@
List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName,
final int userId) {
- // Static RROs targeting "android" are loaded from AssetManager, and so they should be
- // ignored in OverlayManagerService.
return selectWhereTarget(targetPackageName, userId)
- .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
.map(SettingsItem::getOverlayInfo)
.collect(Collectors.toList());
}
ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
- // Static RROs targeting "android" are loaded from AssetManager, and so they should be
- // ignored in OverlayManagerService.
return selectWhereUser(userId)
- .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
.map(SettingsItem::getOverlayInfo)
.collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new,
Collectors.toList()));
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 275f3dc..b490381 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -362,7 +362,7 @@
}
private static boolean shouldShowHiddenApp(ApplicationInfo appInfo) {
- if (appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
+ if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
return false;
}
return true;
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
index b670291..7d34270 100644
--- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -16,10 +16,10 @@
package com.android.server.role;
-import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
import android.app.role.IRoleManagerCallback;
import android.app.role.RoleManagerCallback;
import android.content.ComponentName;
@@ -34,6 +34,7 @@
import android.rolecontrollerservice.RoleControllerService;
import android.util.Slog;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.function.pooled.PooledLambda;
import java.util.ArrayDeque;
@@ -44,9 +45,13 @@
*/
public class RemoteRoleControllerService {
+ static final boolean DEBUG = false;
private static final String LOG_TAG = RemoteRoleControllerService.class.getSimpleName();
@NonNull
+ private static final Handler sCallbackHandler = BackgroundThread.getHandler();
+
+ @NonNull
private final Connection mConnection;
public RemoteRoleControllerService(@UserIdInt int userId, @NonNull Context context) {
@@ -87,6 +92,16 @@
service.onClearRoleHolders(roleName, callbackDelegate), callback));
}
+ /**
+ * Performs granting of default roles and permissions and appops
+ *
+ * @see RoleControllerService#onGrantDefaultRoles(RoleManagerCallback)
+ */
+ public void onGrantDefaultRoles(@NonNull IRoleManagerCallback callback) {
+ mConnection.enqueueCall(
+ new Connection.Call(IRoleControllerService::onGrantDefaultRoles, callback));
+ }
+
private static final class Connection implements ServiceConnection {
private static final long UNBIND_DELAY_MILLIS = 15 * 1000;
@@ -106,9 +121,6 @@
private final Queue<Call> mPendingCalls = new ArrayDeque<>();
@NonNull
- private final Handler mMainHandler = Handler.getMain();
-
- @NonNull
private final Runnable mUnbindRunnable = this::unbind;
Connection(@UserIdInt int userId, @NonNull Context context) {
@@ -116,14 +128,14 @@
mContext = context;
}
- @MainThread
@Override
+ @WorkerThread
public void onServiceConnected(@NonNull ComponentName name, @NonNull IBinder service) {
mService = IRoleControllerService.Stub.asInterface(service);
executePendingCalls();
}
- @MainThread
+ @WorkerThread
private void executePendingCalls() {
while (!mPendingCalls.isEmpty()) {
Call call = mPendingCalls.poll();
@@ -132,26 +144,33 @@
scheduleUnbind();
}
- @MainThread
@Override
+ @WorkerThread
public void onServiceDisconnected(@NonNull ComponentName name) {
mService = null;
}
- @MainThread
@Override
+ @WorkerThread
public void onBindingDied(@NonNull ComponentName name) {
unbind();
}
public void enqueueCall(@NonNull Call call) {
- mMainHandler.post(PooledLambda.obtainRunnable(this::executeCall, call));
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Enqueue " + call);
+ }
+ sCallbackHandler.executeOrSendMessage(PooledLambda.obtainMessage(
+ Connection::executeCall, this, call));
}
- @MainThread
+ @WorkerThread
private void executeCall(@NonNull Call call) {
ensureBound();
if (mService == null) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Delaying until service connected: " + call);
+ }
mPendingCalls.offer(call);
return;
}
@@ -159,24 +178,28 @@
scheduleUnbind();
}
- @MainThread
+ @WorkerThread
private void ensureBound() {
- mMainHandler.removeCallbacks(mUnbindRunnable);
+ sCallbackHandler.removeCallbacks(mUnbindRunnable);
if (!mBound) {
Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
intent.setPackage(mContext.getPackageManager()
.getPermissionControllerPackageName());
+ // Use direct handler to ensure onServiceConnected callback happens in the same
+ // call frame, as required by onGrantDefaultRoles
+ //
+ // Note that as a result, onServiceConnected may happen not on main thread!
mBound = mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
- UserHandle.of(mUserId));
+ sCallbackHandler, UserHandle.of(mUserId));
}
}
private void scheduleUnbind() {
- mMainHandler.removeCallbacks(mUnbindRunnable);
- mMainHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS);
+ sCallbackHandler.removeCallbacks(mUnbindRunnable);
+ sCallbackHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS);
}
- @MainThread
+ @WorkerThread
private void unbind() {
if (mBound) {
mService = null;
@@ -196,9 +219,6 @@
private final IRoleManagerCallback mCallback;
@NonNull
- private final Handler mMainHandler = Handler.getMain();
-
- @NonNull
private final Runnable mTimeoutRunnable = () -> notifyCallback(false);
private boolean mCallbackNotified;
@@ -209,10 +229,13 @@
mCallback = callback;
}
- @MainThread
+ @WorkerThread
public void execute(IRoleControllerService service) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Executing " + this);
+ }
try {
- mMainHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MILLIS);
+ sCallbackHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MILLIS);
mCallExecutor.execute(service, new CallbackDelegate());
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Error calling RoleControllerService", e);
@@ -220,13 +243,13 @@
}
}
- @MainThread
+ @WorkerThread
private void notifyCallback(boolean success) {
if (mCallbackNotified) {
return;
}
mCallbackNotified = true;
- mMainHandler.removeCallbacks(mTimeoutRunnable);
+ sCallbackHandler.removeCallbacks(mTimeoutRunnable);
try {
if (success) {
mCallback.onSuccess();
@@ -239,10 +262,15 @@
}
}
+ @Override
+ public String toString() {
+ return "Call with callback: " + mCallback;
+ }
+
@FunctionalInterface
public interface CallExecutor {
- @MainThread
+ @WorkerThread
void execute(IRoleControllerService service, IRoleManagerCallback callbackDelegate)
throws RemoteException;
}
@@ -251,13 +279,14 @@
@Override
public void onSuccess() throws RemoteException {
- mMainHandler.post(PooledLambda.obtainRunnable(Call.this::notifyCallback, true));
+ sCallbackHandler.sendMessage(PooledLambda.obtainMessage(
+ Call::notifyCallback, Call.this, true));
}
@Override
public void onFailure() throws RemoteException {
- mMainHandler.post(PooledLambda.obtainRunnable(Call.this::notifyCallback,
- false));
+ sCallbackHandler.sendMessage(PooledLambda.obtainMessage(
+ Call::notifyCallback, Call.this, false));
}
}
}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index ded075d..d01e762 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -45,6 +45,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Service for role management.
@@ -105,17 +109,37 @@
public void onStart() {
publishBinderService(Context.ROLE_SERVICE, new Stub());
//TODO add watch for new user creation and run default grants for them
+ //TODO add package update watch to detect PermissionController upgrade and run def. grants
}
@Override
public void onStartUser(@UserIdInt int userId) {
synchronized (mLock) {
//TODO only call into PermissionController if it or system upgreaded (for boot time)
- // (add package changes watch;
- // we can detect upgrade using build fingerprint and app version)
getUserStateLocked(userId);
- //TODO call permission grant policy here
+ }
+ //TODO consider calling grants only when certain conditions are met
+ // such as OS or PermissionController upgrade
+ if (RemoteRoleControllerService.DEBUG) {
Slog.i(LOG_TAG, "Granting default permissions...");
+ CompletableFuture<Void> result = new CompletableFuture<>();
+ getControllerService(userId).onGrantDefaultRoles(
+ new IRoleManagerCallback.Stub() {
+ @Override
+ public void onSuccess() {
+ result.complete(null);
+ }
+
+ @Override
+ public void onFailure() {
+ result.completeExceptionally(new RuntimeException());
+ }
+ });
+ try {
+ result.get(5, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e);
+ }
}
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 70220d8..3050409 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -47,6 +47,7 @@
import android.net.NetworkStats;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiActivityEnergyInfo;
+import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Bundle;
@@ -87,6 +88,8 @@
import com.android.internal.app.procstats.IProcessStats;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BinderCallsStats.ExportedCallStat;
import com.android.internal.os.KernelCpuSpeedReader;
import com.android.internal.os.KernelCpuThreadReader;
@@ -205,6 +208,10 @@
@Nullable
private final KernelCpuThreadReader mKernelCpuThreadReader;
+ private BatteryStatsHelper mBatteryStatsHelper = null;
+ private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
+ private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
+
private static IThermalService sThermalService;
private File mBaseDir =
new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
@@ -1449,6 +1456,73 @@
pulledData.add(e);
}
+ private BatteryStatsHelper getBatteryStatsHelper() {
+ if (mBatteryStatsHelper == null) {
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
+ mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ mBatteryStatsHelper.create((Bundle) null);
+ }
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
+ // Load BatteryStats and do all the calculations.
+ mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL);
+ // Calculations are done so we don't need to save the raw BatteryStats data in RAM.
+ mBatteryStatsHelper.clearStats();
+ mBatteryStatsHelperTimestampMs = currentTime;
+ }
+ return mBatteryStatsHelper;
+ }
+
+ private void pullDeviceCalculatedPowerUse(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ BatteryStatsHelper bsHelper = getBatteryStatsHelper();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeFloat((float) bsHelper.getComputedPower());
+ pulledData.add(e);
+ }
+
+ private void pullDeviceCalculatedPowerBlameUid(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+ if (sippers == null) {
+ return;
+ }
+ for (BatterySipper bs : sippers) {
+ if (bs.drainType != bs.drainType.APP) {
+ continue;
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(bs.uidObj.getUid());
+ e.writeFloat((float) bs.totalPowerMah);
+ pulledData.add(e);
+ }
+ }
+
+ private void pullDeviceCalculatedPowerBlameOther(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+ if (sippers == null) {
+ return;
+ }
+ for (BatterySipper bs : sippers) {
+ if (bs.drainType == bs.drainType.APP) {
+ continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
+ }
+ if (bs.drainType == bs.drainType.USER) {
+ continue; // This is not supported. We purposefully calculate over USER_ALL.
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(bs.drainType.ordinal());
+ e.writeFloat((float) bs.totalPowerMah);
+ pulledData.add(e);
+ }
+ }
+
private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
@@ -1674,6 +1748,18 @@
pullCpuTimePerThreadFreq(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.DEVICE_CALCULATED_POWER_USE: {
+ pullDeviceCalculatedPowerUse(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: {
+ pullDeviceCalculatedPowerBlameUid(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: {
+ pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 111c921..92944a0 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -95,6 +95,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.IApplicationToken;
+import android.view.InputApplicationHandle;
import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -105,7 +106,6 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
-import com.android.server.input.InputApplicationHandle;
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
import com.android.server.wm.WindowManagerService.H;
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index ce8c979..7ed078a 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -36,7 +36,7 @@
import android.view.View;
import com.android.internal.util.Preconditions;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputWindowHandle;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
import java.util.concurrent.atomic.AtomicReference;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 5483602..a379266 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -57,8 +57,8 @@
import com.android.internal.view.IDragAndDropPermissions;
import com.android.server.LocalServices;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
import java.util.ArrayList;
@@ -223,7 +223,7 @@
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
+ mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
display.getDisplayId());
mDragWindowHandle.name = "drag";
mDragWindowHandle.inputChannel = mServerChannel;
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 585a4f5..49bedc9 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -23,8 +23,8 @@
import android.view.InputChannel;
import android.view.WindowManager;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
import java.io.PrintWriter;
@@ -63,7 +63,7 @@
mApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mWindowHandle = new InputWindowHandle(mApplicationHandle, null, null, displayId);
+ mWindowHandle = new InputWindowHandle(mApplicationHandle, null, displayId);
mWindowHandle.name = name;
mWindowHandle.inputChannel = mServerChannel;
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index f823caa..92ea1a9 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -8,16 +8,19 @@
import android.app.ActivityManager;
import android.os.Debug;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import android.view.KeyEvent;
import android.view.WindowManager;
-import com.android.server.input.InputApplicationHandle;
+import android.view.InputApplicationHandle;
import com.android.server.input.InputManagerService;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputWindowHandle;
+import android.view.InputChannel;
import java.io.PrintWriter;
+import java.util.HashMap;
final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
private final WindowManagerService mService;
@@ -48,13 +51,13 @@
* Called by the InputManager.
*/
@Override
- public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
- if (inputWindowHandle == null) {
+ public void notifyInputChannelBroken(IBinder token) {
+ if (token == null) {
return;
}
synchronized (mService.mGlobalLock) {
- WindowState windowState = (WindowState) inputWindowHandle.windowState;
+ WindowState windowState = mService.windowForClientLocked(null, token, false);
if (windowState != null) {
Slog.i(TAG_WM, "WINDOW DIED " + windowState);
windowState.removeIfPossible();
@@ -70,13 +73,13 @@
*/
@Override
public long notifyANR(InputApplicationHandle inputApplicationHandle,
- InputWindowHandle inputWindowHandle, String reason) {
+ IBinder token, String reason) {
AppWindowToken appWindowToken = null;
WindowState windowState = null;
boolean aboveSystem = false;
synchronized (mService.mGlobalLock) {
- if (inputWindowHandle != null) {
- windowState = (WindowState) inputWindowHandle.windowState;
+ if (token != null) {
+ windowState = mService.windowForClientLocked(null, token, false);
if (windowState != null) {
appWindowToken = windowState.mAppToken;
}
@@ -188,8 +191,8 @@
*/
@Override
public long interceptKeyBeforeDispatching(
- InputWindowHandle focus, KeyEvent event, int policyFlags) {
- WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
+ IBinder focus, KeyEvent event, int policyFlags) {
+ WindowState windowState = mService.windowForClientLocked(null, focus, false);
return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}
@@ -199,8 +202,8 @@
*/
@Override
public KeyEvent dispatchUnhandledKey(
- InputWindowHandle focus, KeyEvent event, int policyFlags) {
- WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
+ IBinder focus, KeyEvent event, int policyFlags) {
+ WindowState windowState = mService.windowForClientLocked(null, focus, false);
return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0e4ab53..61bc4e4 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -42,9 +42,12 @@
import android.util.Slog;
import android.view.InputChannel;
import android.view.InputEventReceiver;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
+import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index c4fbee9..6627c2d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -43,6 +43,7 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
+import android.view.InputWindowHandle;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
@@ -50,7 +51,6 @@
import android.view.SurfaceControl.Transaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
-import com.android.server.input.InputWindowHandle;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.InsetUtils;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 66ebc9b..7182ad6 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -49,8 +49,9 @@
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
+import com.android.server.wm.WindowManagerService.H;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -265,7 +266,7 @@
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
+ mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
display.getDisplayId());
mDragWindowHandle.name = TAG;
mDragWindowHandle.inputChannel = mServerChannel;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index f2d3dca..51567a0 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -29,7 +29,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.input.InputManagerService;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputWindowHandle;
/**
* Controller for task positioning by drag.
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2b5076a..a117cf3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -191,7 +191,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputWindowHandle;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import com.android.server.wm.utils.InsetUtils;
@@ -718,7 +718,7 @@
mLastRequestedHeight = 0;
mLayer = 0;
mInputWindowHandle = new InputWindowHandle(
- mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
+ mAppToken != null ? mAppToken.mInputApplicationHandle : null, c,
getDisplayId());
}
@@ -2047,7 +2047,7 @@
// Create dummy event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
- mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
+ mService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
}
void disposeInputChannel() {
@@ -2059,6 +2059,7 @@
// unregister server channel first otherwise it complains about broken channel
if (mInputChannel != null) {
mService.mInputManager.unregisterInputChannel(mInputChannel);
+
mInputChannel.dispose();
mInputChannel = null;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 3943dba..e2db807 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -42,6 +42,8 @@
#include <utils/Trace.h>
#include <utils/SortedVector.h>
+#include <binder/IServiceManager.h>
+
#include <input/PointerController.h>
#include <input/SpriteController.h>
@@ -63,6 +65,7 @@
#include "android_hardware_input_InputApplicationHandle.h"
#include "android_hardware_input_InputWindowHandle.h"
#include "android_hardware_display_DisplayViewport.h"
+#include "android_util_Binder.h"
#include <vector>
@@ -153,15 +156,6 @@
getInputApplicationHandleObjLocalRef(env);
}
-static jobject getInputWindowHandleObjLocalRef(JNIEnv* env,
- const sp<InputWindowHandle>& inputWindowHandle) {
- if (inputWindowHandle == nullptr) {
- return nullptr;
- }
- return static_cast<NativeInputWindowHandle*>(inputWindowHandle.get())->
- getInputWindowHandleObjLocalRef(env);
-}
-
static void loadSystemIconAsSpriteWithPointerIcon(JNIEnv* env, jobject contextObj, int32_t style,
PointerIcon* outPointerIcon, SpriteIcon* outSpriteIcon) {
status_t status = android_view_PointerIcon_loadSystemIcon(env,
@@ -216,8 +210,7 @@
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
- status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId);
+ status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, int32_t displayId);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
@@ -253,17 +246,17 @@
uint32_t policyFlags);
virtual void notifyConfigurationChanged(nsecs_t when);
virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<InputWindowHandle>& inputWindowHandle,
+ const sp<IBinder>& token,
const std::string& reason);
- virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle);
+ virtual void notifyInputChannelBroken(const sp<IBinder>& token);
virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
virtual nsecs_t interceptKeyBeforeDispatching(
- const sp<InputWindowHandle>& inputWindowHandle,
+ const sp<IBinder>& token,
const KeyEvent* keyEvent, uint32_t policyFlags);
- virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
+ virtual bool dispatchUnhandledKey(const sp<IBinder>& token,
const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent);
virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
virtual bool checkInjectEventsPermissionNonReentrant(
@@ -442,11 +435,10 @@
}
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
- const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle,
- int32_t displayId) {
+ const sp<InputChannel>& inputChannel, int32_t displayId) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->registerInputChannel(inputChannel, inputWindowHandle,
- displayId);
+ return mInputManager->getDispatcher()->registerInputChannel(
+ inputChannel, displayId);
}
status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
@@ -657,7 +649,7 @@
}
nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<InputWindowHandle>& inputWindowHandle, const std::string& reason) {
+ const sp<IBinder>& token, const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyANR");
#endif
@@ -667,12 +659,11 @@
jobject inputApplicationHandleObj =
getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
- jobject inputWindowHandleObj =
- getInputWindowHandleObjLocalRef(env, inputWindowHandle);
+ jobject tokenObj = javaObjectForIBinder(env, token);
jstring reasonObj = env->NewStringUTF(reason.c_str());
jlong newTimeout = env->CallLongMethod(mServiceObj,
- gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj,
+ gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj,
reasonObj);
if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
newTimeout = 0; // abort dispatch
@@ -681,12 +672,11 @@
}
env->DeleteLocalRef(reasonObj);
- env->DeleteLocalRef(inputWindowHandleObj);
env->DeleteLocalRef(inputApplicationHandleObj);
return newTimeout;
}
-void NativeInputManager::notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) {
+void NativeInputManager::notifyInputChannelBroken(const sp<IBinder>& token) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyInputChannelBroken");
#endif
@@ -694,14 +684,11 @@
JNIEnv* env = jniEnv();
- jobject inputWindowHandleObj =
- getInputWindowHandleObjLocalRef(env, inputWindowHandle);
- if (inputWindowHandleObj) {
+ jobject tokenObj = javaObjectForIBinder(env, token);
+ if (tokenObj) {
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyInputChannelBroken,
- inputWindowHandleObj);
+ tokenObj);
checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken");
-
- env->DeleteLocalRef(inputWindowHandleObj);
}
}
@@ -1061,7 +1048,7 @@
}
nsecs_t NativeInputManager::interceptKeyBeforeDispatching(
- const sp<InputWindowHandle>& inputWindowHandle,
+ const sp<IBinder>& token,
const KeyEvent* keyEvent, uint32_t policyFlags) {
ATRACE_CALL();
// Policy:
@@ -1072,13 +1059,14 @@
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
- // Note: inputWindowHandle may be null.
- jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef(env, inputWindowHandle);
+ // Token may be null
+ jobject tokenObj = javaObjectForIBinder(env, token);
+
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
if (keyEventObj) {
jlong delayMillis = env->CallLongMethod(mServiceObj,
gServiceClassInfo.interceptKeyBeforeDispatching,
- inputWindowHandleObj, keyEventObj, policyFlags);
+ tokenObj, keyEventObj, policyFlags);
bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
@@ -1092,12 +1080,11 @@
} else {
ALOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
}
- env->DeleteLocalRef(inputWindowHandleObj);
}
return result;
}
-bool NativeInputManager::dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
+bool NativeInputManager::dispatchUnhandledKey(const sp<IBinder>& token,
const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) {
ATRACE_CALL();
// Policy:
@@ -1106,13 +1093,13 @@
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
- // Note: inputWindowHandle may be null.
- jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef(env, inputWindowHandle);
+ // Note: tokenObj may be null.
+ jobject tokenObj = javaObjectForIBinder(env, token);
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
if (keyEventObj) {
jobject fallbackKeyEventObj = env->CallObjectMethod(mServiceObj,
gServiceClassInfo.dispatchUnhandledKey,
- inputWindowHandleObj, keyEventObj, policyFlags);
+ tokenObj, keyEventObj, policyFlags);
if (checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey")) {
fallbackKeyEventObj = nullptr;
}
@@ -1131,7 +1118,6 @@
} else {
ALOGE("Failed to obtain key event object for dispatchUnhandledKey.");
}
- env->DeleteLocalRef(inputWindowHandleObj);
}
return result;
}
@@ -1316,7 +1302,7 @@
}
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jint displayId) {
+ jlong ptr, jobject inputChannelObj, jint displayId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
@@ -1325,12 +1311,10 @@
throwInputChannelNotInitialized(env);
return;
}
+ bool monitor = inputChannel->getToken() == nullptr && displayId != ADISPLAY_ID_NONE;
- sp<InputWindowHandle> inputWindowHandle =
- android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
+ status_t status = im->registerInputChannel(env, inputChannel, displayId);
- status_t status = im->registerInputChannel(
- env, inputChannel, inputWindowHandle, displayId);
if (status) {
std::string message;
message += StringPrintf("Failed to register input channel. status=%d", status);
@@ -1339,7 +1323,7 @@
}
// If inputWindowHandle is null and displayId >= 0, treat inputChannel as monitor.
- if (inputWindowHandle != nullptr || displayId == ADISPLAY_ID_NONE) {
+ if (!monitor) {
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
}
@@ -1640,7 +1624,7 @@
{ "nativeHasKeys", "(JII[I[Z)Z",
(void*) nativeHasKeys },
{ "nativeRegisterInputChannel",
- "(JLandroid/view/InputChannel;Lcom/android/server/input/InputWindowHandle;I)V",
+ "(JLandroid/view/InputChannel;I)V",
(void*) nativeRegisterInputChannel },
{ "nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V",
(void*) nativeUnregisterInputChannel },
@@ -1650,9 +1634,9 @@
(void*) nativeInjectInputEvent },
{ "nativeToggleCapsLock", "(JI)V",
(void*) nativeToggleCapsLock },
- { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;I)V",
+ { "nativeSetInputWindows", "(J[Landroid/view/InputWindowHandle;I)V",
(void*) nativeSetInputWindows },
- { "nativeSetFocusedApplication", "(JILcom/android/server/input/InputApplicationHandle;)V",
+ { "nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V",
(void*) nativeSetFocusedApplication },
{ "nativeSetFocusedDisplay", "(JI)V",
(void*) nativeSetFocusedDisplay },
@@ -1731,11 +1715,11 @@
"notifySwitch", "(JII)V");
GET_METHOD_ID(gServiceClassInfo.notifyInputChannelBroken, clazz,
- "notifyInputChannelBroken", "(Lcom/android/server/input/InputWindowHandle;)V");
+ "notifyInputChannelBroken", "(Landroid/os/IBinder;)V");
GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
"notifyANR",
- "(Lcom/android/server/input/InputApplicationHandle;Lcom/android/server/input/InputWindowHandle;Ljava/lang/String;)J");
+ "(Landroid/view/InputApplicationHandle;Landroid/os/IBinder;Ljava/lang/String;)J");
GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz,
"filterInputEvent", "(Landroid/view/InputEvent;I)Z");
@@ -1748,11 +1732,11 @@
GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz,
"interceptKeyBeforeDispatching",
- "(Lcom/android/server/input/InputWindowHandle;Landroid/view/KeyEvent;I)J");
+ "(Landroid/os/IBinder;Landroid/view/KeyEvent;I)J");
GET_METHOD_ID(gServiceClassInfo.dispatchUnhandledKey, clazz,
"dispatchUnhandledKey",
- "(Lcom/android/server/input/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
+ "(Landroid/os/IBinder;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
GET_METHOD_ID(gServiceClassInfo.checkInjectEventsPermission, clazz,
"checkInjectEventsPermission", "(II)Z");
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index dc3cd62..fcfd246 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -19,6 +19,7 @@
import static android.content.Context.INTELLIGENCE_MANAGER_SERVICE;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.content.ComponentName;
import android.content.Context;
@@ -59,14 +60,14 @@
super(context, UserManager.DISALLOW_INTELLIGENCE_CAPTURE);
}
- @Override // from MasterSystemService
+ @Override // from AbstractMasterSystemService
protected String getServiceSettingsProperty() {
// TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd
return "intel_service";
}
- @Override // from MasterSystemService
- protected IntelligencePerUserService newServiceLocked(int resolvedUserId,
+ @Override // from AbstractMasterSystemService
+ protected IntelligencePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
return new IntelligencePerUserService(this, mLock, resolvedUserId);
}
@@ -79,12 +80,9 @@
}
@Override // from AbstractMasterSystemService
- protected IntelligencePerUserService removeCachedServiceLocked(int userId) {
- final IntelligencePerUserService service = super.removeCachedServiceLocked(userId);
- if (service != null) {
- service.destroyLocked();
- }
- return service;
+ protected void onServiceRemoved(@NonNull IntelligencePerUserService service,
+ @UserIdInt int userId) {
+ service.destroyLocked();
}
private ActivityManagerInternal getAmInternal() {
diff --git a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
deleted file mode 100644
index c162c3b..0000000
--- a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import android.content.ContentResolver;
-import android.provider.Settings;
-import android.test.mock.MockContentResolver;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.test.FakeSettingsProvider;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Tests for {@link GlobalSettingsToPropertiesMapper}
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:GlobalSettingsToPropertiesMapperTest
- */
-@SmallTest
-public class GlobalSettingsToPropertiesMapperTest {
- private static final String[][] TEST_MAPPING = new String[][] {
- {Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "TestProperty"}
- };
-
- private TestMapper mTestMapper;
- private MockContentResolver mMockContentResolver;
-
- @Before
- public void setup() {
- // Use FakeSettingsProvider to not affect global state
- mMockContentResolver = new MockContentResolver(getInstrumentation().getTargetContext());
- mMockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- mTestMapper = new TestMapper(mMockContentResolver);
- }
-
- @Test
- public void testUpdatePropertiesFromGlobalSettings() {
- Settings.Global.putString(mMockContentResolver,
- Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
-
- mTestMapper.updatePropertiesFromGlobalSettings();
- String propValue = mTestMapper.systemPropertiesGet("TestProperty");
- assertEquals("testValue", propValue);
-
- Settings.Global.putString(mMockContentResolver,
- Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
- mTestMapper.updatePropertyFromSetting(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
- "TestProperty");
- propValue = mTestMapper.systemPropertiesGet("TestProperty");
- assertEquals("testValue2", propValue);
-
- Settings.Global.putString(mMockContentResolver,
- Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
- mTestMapper.updatePropertyFromSetting(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
- "TestProperty");
- propValue = mTestMapper.systemPropertiesGet("TestProperty");
- assertEquals("", propValue);
- }
-
- @Test
- public void testUpdatePropertiesFromGlobalSettings_PropertyAndSettingNotPresent() {
- // Test that empty property will not not be set if setting is not set
- mTestMapper.updatePropertiesFromGlobalSettings();
- String propValue = mTestMapper.systemPropertiesGet("TestProperty");
- assertNull("Property should not be set if setting is null", propValue);
- }
-
- private static class TestMapper extends GlobalSettingsToPropertiesMapper {
- private final Map<String, String> mProps = new HashMap<>();
-
- TestMapper(ContentResolver contentResolver) {
- super(contentResolver, TEST_MAPPING);
- }
-
- @Override
- protected String systemPropertiesGet(String key) {
- Preconditions.checkNotNull(key);
- return mProps.get(key);
- }
-
- @Override
- protected void systemPropertiesSet(String key, String value) {
- Preconditions.checkNotNull(value);
- Preconditions.checkNotNull(key);
- mProps.put(key, value);
- }
- }
-}
-
diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
new file mode 100644
index 0000000..d965f8a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.text.TextUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tests for {@link SettingsToPropertiesMapper}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SettingsToPropertiesMapperTest {
+ private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$";
+ private static final String[] TEST_MAPPING = new String[] {
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS
+ };
+
+ private TestMapper mTestMapper;
+ private MockContentResolver mMockContentResolver;
+
+ @Before
+ public void setupForEach() {
+ // Use FakeSettingsProvider to not affect global state
+ mMockContentResolver = new MockContentResolver(InstrumentationRegistry.getContext());
+ mMockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ mTestMapper = new TestMapper(mMockContentResolver);
+ }
+
+ @Test
+ public void validateRegisteredGlobalSettings() {
+ HashSet<String> hashSet = new HashSet<>();
+ for (String globalSetting : SettingsToPropertiesMapper.sGlobalSettings) {
+ if (hashSet.contains(globalSetting)) {
+ Assert.fail("globalSetting "
+ + globalSetting
+ + " is registered more than once in "
+ + "SettingsToPropertiesMapper.sGlobalSettings.");
+ }
+ hashSet.add(globalSetting);
+ if (TextUtils.isEmpty(globalSetting)) {
+ Assert.fail("empty globalSetting registered.");
+ }
+ if (!globalSetting.matches(NAME_VALID_CHARACTERS_REGEX)) {
+ Assert.fail(globalSetting + " contains invalid characters. "
+ + "Only alphanumeric characters, '.', '-', '@', ':' and '_' are valid.");
+ }
+ }
+ }
+
+ @Test
+ public void testUpdatePropertiesFromSettings() {
+ Settings.Global.putString(mMockContentResolver,
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
+
+ String systemPropertyName = "persist.device_config.global_settings."
+ + "sqlite_compatibility_wal_flags";
+
+ mTestMapper.updatePropertiesFromSettings();
+ String propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+ Assert.assertEquals("testValue", propValue);
+
+ Settings.Global.putString(mMockContentResolver,
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
+ mTestMapper.updatePropertyFromSetting(
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
+ systemPropertyName,
+ true);
+ propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+ Assert.assertEquals("testValue2", propValue);
+
+ Settings.Global.putString(mMockContentResolver,
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
+ mTestMapper.updatePropertyFromSetting(
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
+ systemPropertyName,
+ true);
+ propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+ Assert.assertEquals("", propValue);
+ }
+
+ @Test
+ public void testMakePropertyName() {
+ try {
+ Assert.assertEquals("persist.device_config.test_category.test_flag",
+ SettingsToPropertiesMapper.makePropertyName("test_category", "test_flag"));
+ } catch (Exception e) {
+ Assert.fail("Unexpected exception: " + e.getMessage());
+ }
+
+ try {
+ Assert.assertEquals(null,
+ SettingsToPropertiesMapper.makePropertyName("test_category!!!", "test_flag"));
+ } catch (Exception e) {
+ Assert.fail("Unexpected exception: " + e.getMessage());
+ }
+
+ try {
+ Assert.assertEquals(null,
+ SettingsToPropertiesMapper.makePropertyName("test_category", ".test_flag"));
+ } catch (Exception e) {
+ Assert.fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testUpdatePropertiesFromSettings_PropertyAndSettingNotPresent() {
+ // Test that empty property will not not be set if setting is not set
+ mTestMapper.updatePropertiesFromSettings();
+ String propValue = mTestMapper.systemPropertiesGet("TestProperty");
+ Assert.assertNull("Property should not be set if setting is null", propValue);
+ }
+
+ @Test
+ public void testIsNativeFlagsResetPerformed() {
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+ Assert.assertTrue(mTestMapper.isNativeFlagsResetPerformed());
+
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "false");
+ Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
+
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "");
+ Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
+ }
+
+ @Test
+ public void testGetResetNativeCategories() {
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "");
+ Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
+
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+ mTestMapper.setFileContent("");
+ Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
+
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+ mTestMapper.setFileContent("persist.device_config.category1.flag;"
+ + "persist.device_config.category2.flag;persist.device_config.category3.flag;"
+ + "persist.device_config.category3.flag2");
+ List<String> categories = Arrays.asList(mTestMapper.getResetNativeCategories());
+ Assert.assertEquals(3, categories.size());
+ Assert.assertTrue(categories.contains("category1"));
+ Assert.assertTrue(categories.contains("category2"));
+ Assert.assertTrue(categories.contains("category3"));
+ }
+
+ private static class TestMapper extends SettingsToPropertiesMapper {
+ private final Map<String, String> mProps = new HashMap<>();
+
+ private String mFileContent = "";
+
+ TestMapper(ContentResolver contentResolver) {
+ super(contentResolver, TEST_MAPPING, new String[] {});
+ }
+
+ @Override
+ protected String systemPropertiesGet(String key) {
+ Preconditions.checkNotNull(key);
+ return mProps.get(key);
+ }
+
+ @Override
+ protected void systemPropertiesSet(String key, String value) {
+ Preconditions.checkNotNull(value);
+ Preconditions.checkNotNull(key);
+ mProps.put(key, value);
+ }
+
+ protected void setFileContent(String fileContent) {
+ mFileContent = fileContent;
+ }
+
+ @Override
+ protected String getResetFlagsFileContent() {
+ return mFileContent;
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
index f17a30d..410ab87 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
@@ -72,7 +72,7 @@
assertTrue(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
assertEquals(people, r.getPeopleOverride());
assertEquals(snoozeCriteria, r.getSnoozeCriteria());
- assertEquals(smartActions, r.getSmartActions());
+ assertEquals(smartActions, r.getSystemGeneratedSmartActions());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 9b41fdd..8690110 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -708,14 +708,14 @@
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /* defaultLights */, groupId /* group */);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- assertNull(record.getSmartActions());
+ assertNull(record.getSystemGeneratedSmartActions());
ArrayList<Notification.Action> smartActions = new ArrayList<>();
smartActions.add(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.btn_default),
"text", null).build());
- record.setSmartActions(smartActions);
- assertEquals(smartActions, record.getSmartActions());
+ record.setSystemGeneratedSmartActions(smartActions);
+ assertEquals(smartActions, record.getSystemGeneratedSmartActions());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 3fe381b..1a218b2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -27,7 +27,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -98,7 +97,7 @@
private static final UserHandle USER = UserHandle.of(0);
private static final int UID_O = 1111;
private static final String SYSTEM_PKG = "android";
- private static final int SYSTEM_UID= 1000;
+ private static final int SYSTEM_UID = 1000;
private static final UserHandle USER2 = UserHandle.of(10);
private static final String TEST_CHANNEL_ID = "test_channel_id";
private static final String TEST_AUTHORITY = "test";
@@ -1091,6 +1090,158 @@
}
@Test
+ public void testGetChannelsBypassingDndCount_noChannelsBypassing() throws Exception {
+ assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ USER.getIdentifier()).getList().size());
+ }
+
+ @Test
+ public void testGetChannelsBypassingDnd_noChannelsForUserIdBypassing()
+ throws Exception {
+ int user = 9;
+ NotificationChannel channel = new NotificationChannel("id", "name",
+ NotificationManager.IMPORTANCE_MAX);
+ channel.setBypassDnd(true);
+ mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true);
+
+ assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+ }
+
+ @Test
+ public void testGetChannelsBypassingDndCount_oneChannelBypassing_groupBlocked() {
+ int user = USER.getIdentifier();
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel1.setGroup(ncg.getId());
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+
+ assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+
+ // disable group
+ ncg.setBlocked(true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ false);
+ assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+ }
+
+ @Test
+ public void testGetChannelsBypassingDndCount_multipleChannelsBypassing() {
+ int user = USER.getIdentifier();
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel2 = new NotificationChannel("id2", "name2",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel3 = new NotificationChannel("id3", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel2.setBypassDnd(true);
+ channel3.setBypassDnd(true);
+ // has DND access, so can set bypassDnd attribute
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true);
+ assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+
+ // block notifications from this app
+ mHelper.setEnabled(PKG_N_MR1, user, false);
+ assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+
+ // re-enable notifications from this app
+ mHelper.setEnabled(PKG_N_MR1, user, true);
+ assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+
+ // setBypassDnd false for some channels
+ channel1.setBypassDnd(false);
+ channel2.setBypassDnd(false);
+ assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+
+ // setBypassDnd false for rest of the channels
+ channel3.setBypassDnd(false);
+ assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+ }
+
+ @Test
+ public void testGetAppsBypassingDndCount_noAppsBypassing() throws Exception {
+ assertEquals(0, mHelper.getAppsBypassingDndCount(USER.getIdentifier()));
+ }
+
+ @Test
+ public void testGetAppsBypassingDndCount_noAppsForUserIdBypassing() throws Exception {
+ int user = 9;
+ NotificationChannel channel = new NotificationChannel("id", "name",
+ NotificationManager.IMPORTANCE_MAX);
+ channel.setBypassDnd(true);
+ mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true);
+
+ assertEquals(0, mHelper.getAppsBypassingDndCount(user));
+ }
+
+ @Test
+ public void testGetAppsBypassingDndCount_oneChannelBypassing_groupBlocked() {
+ int user = USER.getIdentifier();
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel1.setGroup(ncg.getId());
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+
+ assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+ // disable group
+ ncg.setBlocked(true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ false);
+ assertEquals(0, mHelper.getAppsBypassingDndCount(user));
+ }
+
+ @Test
+ public void testGetAppsBypassingDndCount_oneAppBypassing() {
+ int user = USER.getIdentifier();
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel2 = new NotificationChannel("id2", "name2",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel3 = new NotificationChannel("id3", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel2.setBypassDnd(true);
+ channel3.setBypassDnd(true);
+ // has DND access, so can set bypassDnd attribute
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true);
+ assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+ // block notifications from this app
+ mHelper.setEnabled(PKG_N_MR1, user, false);
+ assertEquals(0, mHelper.getAppsBypassingDndCount(user)); // no apps can bypass dnd
+
+ // re-enable notifications from this app
+ mHelper.setEnabled(PKG_N_MR1, user, true);
+ assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+ // setBypassDnd false for some channels
+ channel1.setBypassDnd(false);
+ channel2.setBypassDnd(false);
+ assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+ // setBypassDnd false for rest of the channels
+ channel3.setBypassDnd(false);
+ assertEquals(0, mHelper.getAppsBypassingDndCount(user));
+ }
+
+ @Test
public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
// create notification channel that can't bypass dnd
// expected result: areChannelsBypassingDnd = false
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 32875da..94d7dbb 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -915,8 +915,12 @@
return "SCREEN_INTERACTIVE";
case UsageEvents.Event.SCREEN_NON_INTERACTIVE:
return "SCREEN_NON_INTERACTIVE";
+ case UsageEvents.Event.KEYGUARD_SHOWN:
+ return "KEYGUARD_SHOWN";
+ case UsageEvents.Event.KEYGUARD_HIDDEN:
+ return "KEYGUARD_HIDDEN";
default:
- return "UNKNOWN";
+ return "UNKNOWN_TYPE_" + eventType;
}
}
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 85357bb..906d64c 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -58,12 +58,18 @@
case Instruction::Op::kInvokeVirtual:
out << "kInvokeVirtual";
return out;
+ case Instruction::Op::kInvokeDirect:
+ out << "kInvokeDirect";
+ return out;
case Instruction::Op::kBindLabel:
out << "kBindLabel";
return out;
case Instruction::Op::kBranchEqz:
out << "kBranchEqz";
return out;
+ case Instruction::Op::kNew:
+ out << "kNew";
+ return out;
}
}
@@ -167,6 +173,8 @@
ir::Type* type = Alloc<ir::Type>();
type->descriptor = GetOrAddString(descriptor);
types_by_descriptor_[descriptor] = type;
+ type->orig_index = dex_file_->types_indexes.AllocateIndex();
+ dex_file_->types_map[type->orig_index] = type;
return type;
}
@@ -223,9 +231,10 @@
decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0;
code->registers = num_registers_ + num_args;
code->ins_count = num_args;
- code->outs_count = decl_->prototype->return_type == dex_->GetOrAddType("V") ? 0 : 1;
EncodeInstructions();
code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
+ size_t const return_count = decl_->prototype->return_type == dex_->GetOrAddType("V") ? 0 : 1;
+ code->outs_count = std::max(return_count, max_args_);
method->code = code;
class_->direct_methods.push_back(method);
@@ -277,22 +286,26 @@
case Instruction::Op::kMove:
return EncodeMove(instruction);
case Instruction::Op::kInvokeVirtual:
- return EncodeInvokeVirtual(instruction);
+ return EncodeInvoke(instruction, art::Instruction::INVOKE_VIRTUAL);
+ case Instruction::Op::kInvokeDirect:
+ return EncodeInvoke(instruction, art::Instruction::INVOKE_DIRECT);
case Instruction::Op::kBindLabel:
return BindLabel(instruction.args()[0]);
case Instruction::Op::kBranchEqz:
return EncodeBranch(art::Instruction::IF_EQZ, instruction);
+ case Instruction::Op::kNew:
+ return EncodeNew(instruction);
}
}
void MethodBuilder::EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode) {
DCHECK(!instruction.dest().has_value());
if (instruction.args().size() == 0) {
- buffer_.push_back(art::Instruction::RETURN_VOID);
+ Encode10x(art::Instruction::RETURN_VOID);
} else {
DCHECK_EQ(1, instruction.args().size());
size_t source = RegisterValue(instruction.args()[0]);
- buffer_.push_back(opcode | source << 8);
+ Encode11x(opcode, source);
}
}
@@ -307,37 +320,43 @@
if (source.is_immediate()) {
// TODO: support more registers
DCHECK_LT(RegisterValue(*instruction.dest()), 16);
- DCHECK_LT(source.value(), 16);
- buffer_.push_back(art::Instruction::CONST_4 | (source.value() << 12) |
- (RegisterValue(*instruction.dest()) << 8));
+ Encode11n(art::Instruction::CONST_4, RegisterValue(*instruction.dest()), source.value());
} else if (source.is_string()) {
constexpr size_t kMaxRegisters = 256;
DCHECK_LT(RegisterValue(*instruction.dest()), kMaxRegisters);
DCHECK_LT(source.value(), 65536); // make sure we don't need a jumbo string
- buffer_.push_back(::art::Instruction::CONST_STRING | (RegisterValue(*instruction.dest()) << 8));
- buffer_.push_back(source.value());
+ Encode21c(::art::Instruction::CONST_STRING, RegisterValue(*instruction.dest()), source.value());
} else {
UNIMPLEMENTED(FATAL);
}
}
-void MethodBuilder::EncodeInvokeVirtual(const Instruction& instruction) {
- DCHECK_EQ(Instruction::Op::kInvokeVirtual, instruction.opcode());
+void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode) {
+ constexpr size_t kMaxArgs = 5;
- // TODO: support more than one argument (i.e. the this argument) and change this to DCHECK_GE
- DCHECK_EQ(1, instruction.args().size());
+ CHECK_LE(instruction.args().size(), kMaxArgs);
- const Value& this_arg = instruction.args()[0];
-
- size_t real_reg = RegisterValue(this_arg) & 0xf;
- buffer_.push_back(1 << 12 | art::Instruction::INVOKE_VIRTUAL);
- buffer_.push_back(instruction.method_id());
- buffer_.push_back(real_reg);
-
- if (instruction.dest().has_value()) {
- real_reg = RegisterValue(*instruction.dest());
- buffer_.push_back(real_reg << 8 | art::Instruction::MOVE_RESULT);
+ uint8_t arguments[kMaxArgs]{};
+ for (size_t i = 0; i < instruction.args().size(); ++i) {
+ CHECK(instruction.args()[i].is_variable());
+ arguments[i] = RegisterValue(instruction.args()[i]);
}
+
+ Encode35c(opcode,
+ instruction.args().size(),
+ instruction.method_id(),
+ arguments[0],
+ arguments[1],
+ arguments[2],
+ arguments[3],
+ arguments[4]);
+
+ // If there is a return value, add a move-result instruction
+ if (instruction.dest().has_value()) {
+ Encode11x(art::Instruction::MOVE_RESULT, RegisterValue(*instruction.dest()));
+ }
+
+ max_args_ = std::max(max_args_, instruction.args().size());
}
// Encodes a conditional branch that tests a single argument.
@@ -350,9 +369,21 @@
CHECK(branch_target.is_label());
size_t instruction_offset = buffer_.size();
- buffer_.push_back(op | (RegisterValue(test_value) << 8));
- size_t field_offset = buffer_.size();
- buffer_.push_back(LabelValue(branch_target, instruction_offset, field_offset));
+ size_t field_offset = buffer_.size() + 1;
+ Encode21c(
+ op, RegisterValue(test_value), LabelValue(branch_target, instruction_offset, field_offset));
+}
+
+void MethodBuilder::EncodeNew(const Instruction& instruction) {
+ DCHECK_EQ(Instruction::Op::kNew, instruction.opcode());
+ DCHECK(instruction.dest().has_value());
+ DCHECK(instruction.dest()->is_variable());
+ DCHECK_EQ(1, instruction.args().size());
+
+ const Value& type = instruction.args()[0];
+ DCHECK_LT(RegisterValue(*instruction.dest()), 256);
+ DCHECK(type.is_type());
+ Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value());
}
size_t MethodBuilder::RegisterValue(const Value& value) const {
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 31414c8..adf82bf 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -112,6 +112,7 @@
static constexpr Value Immediate(size_t value) { return Value{value, Kind::kImmediate}; }
static constexpr Value String(size_t value) { return Value{value, Kind::kString}; }
static constexpr Value Label(size_t id) { return Value{id, Kind::kLabel}; }
+ static constexpr Value Type(size_t id) { return Value{id, Kind::kType}; }
bool is_register() const { return kind_ == Kind::kLocalRegister; }
bool is_parameter() const { return kind_ == Kind::kParameter; }
@@ -119,11 +120,12 @@
bool is_immediate() const { return kind_ == Kind::kImmediate; }
bool is_string() const { return kind_ == Kind::kString; }
bool is_label() const { return kind_ == Kind::kLabel; }
+ bool is_type() const { return kind_ == Kind::kType; }
size_t value() const { return value_; }
private:
- enum class Kind { kLocalRegister, kParameter, kImmediate, kString, kLabel };
+ enum class Kind { kLocalRegister, kParameter, kImmediate, kString, kLabel, kType };
const size_t value_;
const Kind kind_;
@@ -139,7 +141,16 @@
public:
// The operation performed by this instruction. These are virtual instructions that do not
// correspond exactly to DEX instructions.
- enum class Op { kReturn, kReturnObject, kMove, kInvokeVirtual, kBindLabel, kBranchEqz };
+ enum class Op {
+ kReturn,
+ kReturnObject,
+ kMove,
+ kInvokeVirtual,
+ kInvokeDirect,
+ kBindLabel,
+ kBranchEqz,
+ kNew
+ };
////////////////////////
// Named Constructors //
@@ -160,6 +171,12 @@
Value this_arg, T... args) {
return Instruction{Op::kInvokeVirtual, method_id, dest, this_arg, args...};
}
+ // For direct calls (basically, constructors).
+ template <typename... T>
+ static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest,
+ Value this_arg, T... args) {
+ return Instruction{Op::kInvokeDirect, method_id, dest, this_arg, args...};
+ }
///////////////
// Accessors //
@@ -189,6 +206,12 @@
// Needed for CHECK_EQ, DCHECK_EQ, etc.
std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode);
+// Keeps track of information needed to manipulate or call a method.
+struct MethodDeclData {
+ size_t id;
+ ir::MethodDecl* decl;
+};
+
// Tools to help build methods and their bodies.
class MethodBuilder {
public:
@@ -216,6 +239,8 @@
// const/4
void BuildConst4(Value target, int value);
void BuildConstString(Value target, const std::string& value);
+ template <typename... T>
+ void BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args);
// TODO: add builders for more instructions
@@ -229,8 +254,55 @@
void EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode);
void EncodeMove(const Instruction& instruction);
- void EncodeInvokeVirtual(const Instruction& instruction);
+ void EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode);
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
+ void EncodeNew(const Instruction& instruction);
+
+ // Low-level instruction format encoding. See
+ // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
+ // formats.
+
+ inline void Encode10x(art::Instruction::Code opcode) {
+ // 00|op
+ buffer_.push_back(opcode);
+ }
+
+ inline void Encode11x(art::Instruction::Code opcode, uint8_t a) {
+ // aa|op
+ buffer_.push_back((a << 8) | opcode);
+ }
+
+ inline void Encode11n(art::Instruction::Code opcode, uint8_t a, int8_t b) {
+ // b|a|op
+
+ // Make sure the fields are in bounds (4 bits for a, 4 bits for b).
+ CHECK_LT(a, 16);
+ CHECK_LE(-8, b);
+ CHECK_LT(b, 8);
+
+ buffer_.push_back(((b & 0xf) << 12) | (a << 8) | opcode);
+ }
+
+ inline void Encode21c(art::Instruction::Code opcode, uint8_t a, uint16_t b) {
+ // aa|op|bbbb
+ buffer_.push_back((a << 8) | opcode);
+ buffer_.push_back(b);
+ }
+
+ inline void Encode35c(art::Instruction::Code opcode, size_t a, uint16_t b, uint8_t c, uint8_t d,
+ uint8_t e, uint8_t f, uint8_t g) {
+ // a|g|op|bbbb|f|e|d|c
+
+ CHECK_LE(a, 5);
+ CHECK_LT(c, 16);
+ CHECK_LT(d, 16);
+ CHECK_LT(e, 16);
+ CHECK_LT(f, 16);
+ CHECK_LT(g, 16);
+ buffer_.push_back((a << 12) | (g << 8) | opcode);
+ buffer_.push_back(b);
+ buffer_.push_back((f << 12) | (e << 8) | (d << 4) | c);
+ }
// Converts a register or parameter to its DEX register number.
size_t RegisterValue(const Value& value) const;
@@ -270,6 +342,10 @@
};
std::vector<LabelData> labels_;
+
+ // During encoding, keep track of the largest number of arguments needed, so we can use it for our
+ // outs count
+ size_t max_args_{0};
};
// A helper to build class definitions.
@@ -289,12 +365,6 @@
ir::Class* const class_;
};
-// Keeps track of information needed to manipulate or call a method.
-struct MethodDeclData {
- size_t id;
- ir::MethodDecl* decl;
-};
-
// Builds Dex files from scratch.
class DexBuilder {
public:
@@ -363,6 +433,17 @@
std::map<Prototype, ir::Proto*> proto_map_;
};
+template <typename... T>
+void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args) {
+ MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)};
+ // allocate the object
+ ir::Type* type_def = dex_->GetOrAddType(type.descriptor());
+ AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kNew, target, Value::Type(type_def->orig_index)));
+ // call the constructor
+ AddInstruction(Instruction::InvokeDirect(constructor_data.id, /*dest=*/{}, target, args...));
+};
+
} // namespace dex
} // namespace startop
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 2ccdc6d5..e20f3a9 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -50,6 +50,14 @@
}
@Test
+ public void returnInteger5() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("returnInteger5");
+ Assert.assertEquals(5, method.invoke(null));
+ }
+
+ @Test
public void returnParam() throws Exception {
ClassLoader loader = loadDexFile("simple.dex");
Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index 063a0cf..e2bf43bc 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -53,6 +53,19 @@
}
return5.Encode();
+ // int return5() { return 5; }
+ auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")};
+ auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})};
+ [&](MethodBuilder& method) {
+ Value five{method.MakeRegister()};
+ method.BuildConst4(five, 5);
+ Value object{method.MakeRegister()};
+ method.BuildNew(
+ object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five);
+ method.BuildReturn(object, /*is_object=*/true);
+ }(returnInteger5);
+ returnInteger5.Encode();
+
// // int returnParam(int x) { return x; }
auto returnParam{cbuilder.CreateMethod("returnParam",
Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fbc54ae6..b744770 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1320,18 +1320,13 @@
public static final String KEY_MMS_CLOSE_CONNECTION_BOOL = "mmsCloseConnection";
/**
- * If carriers require differentiate un-provisioned status: cold sim or out of credit sim
- * a package name and activity name can be provided to launch a supported carrier application
- * that check the sim provisioning status
- * The first element is the package name and the second element is the activity name
- * of the provisioning app
- * example:
- * <item>com.google.android.carrierPackageName</item>
- * <item>com.google.android.carrierPackageName.CarrierActivityName</item>
- * The ComponentName of the carrier activity that can setup the device and activate with the
- * network as part of the Setup Wizard flow.
+ * The flatten {@link android.content.ComponentName componentName} of the activity that can
+ * setup the device and activate with the network per carrier requirements.
+ *
+ * e.g, com.google.android.carrierPackageName/.CarrierActivityName
* @hide
*/
+ @SystemApi
public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
/**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 0c8d581..3c5ad84 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -233,34 +233,35 @@
public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000;
/**
- * Listen for changes to the sim voice activation state
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
- * {@more}
- * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
- * fully activated
+ * Listen for changes to the sim voice activation state
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ * {@more}
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+ * fully activated
*
- * @see #onVoiceActivationStateChanged
- * @hide
+ * @see #onVoiceActivationStateChanged
+ * @hide
*/
+ @SystemApi
public static final int LISTEN_VOICE_ACTIVATION_STATE = 0x00020000;
/**
- * Listen for changes to the sim data activation state
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
- * {@more}
- * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
- * fully activated
+ * Listen for changes to the sim data activation state
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ * {@more}
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+ * fully activated
*
- * @see #onDataActivationStateChanged
- * @hide
+ * @see #onDataActivationStateChanged
+ * @hide
*/
public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000;
@@ -569,8 +570,8 @@
* @param state is the current SIM voice activation state
* @hide
*/
- public void onVoiceActivationStateChanged(int state) {
-
+ @SystemApi
+ public void onVoiceActivationStateChanged(@TelephonyManager.SimActivationState int state) {
}
/**
@@ -578,8 +579,7 @@
* @param state is the current SIM data activation state
* @hide
*/
- public void onDataActivationStateChanged(int state) {
-
+ public void onDataActivationStateChanged(@TelephonyManager.SimActivationState int state) {
}
/**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 31770ec..b5cbf3b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2290,7 +2290,7 @@
* subscription dynamically in multi-SIM devices.
*
* @param subId which subscription is preferred to for cellular data. If it's
- * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}, it means
+ * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, it means
* it's unset and {@link SubscriptionManager#getDefaultDataSubscriptionId()}
* is used to determine which modem is preferred.
* @hide
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index c9cf473..e06c372 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -292,7 +292,7 @@
* Create an instance of ImsManager for the subscription id specified.
*
* @param context
- * @param subId The ID of the subscription that this ImsManager will use.
+ * @param subId The ID of the subscription that this ImsMmTelManager will use.
* @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
* @throws IllegalArgumentException if the subscription is invalid or
* the subscription ID is not an active subscription.
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
new file mode 100644
index 0000000..916e282
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.stub.ImsConfigImplBase;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manages IMS provisioning and configuration parameters, as well as callbacks for apps to listen
+ * to changes in these configurations.
+ *
+ * Note: IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
+ * applications and may vary.
+ * @hide
+ */
+@SystemApi
+public class ProvisioningManager {
+
+ /**
+ * Callback for IMS provisioning changes.
+ */
+ public static class Callback {
+
+ private static class CallbackBinder extends IImsConfigCallback.Stub {
+
+ private final Callback mLocalConfigurationCallback;
+ private Executor mExecutor;
+
+ private CallbackBinder(Callback localConfigurationCallback) {
+ mLocalConfigurationCallback = localConfigurationCallback;
+ }
+
+ @Override
+ public final void onIntConfigChanged(int item, int value) {
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ mLocalConfigurationCallback.onProvisioningIntChanged(item, value)));
+ }
+
+ @Override
+ public final void onStringConfigChanged(int item, String value) {
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ mLocalConfigurationCallback.onProvisioningStringChanged(item,
+ value)));
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final CallbackBinder mBinder = new CallbackBinder(this);
+
+ /**
+ * Called when a provisioning item has changed.
+ * @param item the IMS provisioning key constant, as defined by the OEM.
+ * @param value the new integer value of the IMS provisioning key.
+ */
+ public void onProvisioningIntChanged(int item, int value) {
+ // Base Implementation
+ }
+
+ /**
+ * Called when a provisioning item has changed.
+ * @param item the IMS provisioning key constant, as defined by the OEM.
+ * @param value the new String value of the IMS configuration constant.
+ */
+ public void onProvisioningStringChanged(int item, String value) {
+ // Base Implementation
+ }
+
+ /**@hide*/
+ public final IImsConfigCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ private int mSubId;
+
+ /**
+ * Create a new {@link ProvisioningManager} for the subscription specified.
+ * @param context The context that this manager will use.
+ * @param subId The ID of the subscription that this ProvisioningManager will use.
+ * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+ * @throws IllegalArgumentException if the subscription is invalid or
+ * the subscription ID is not an active subscription.
+ */
+ public static ProvisioningManager createForSubscriptionId(Context context, int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)
+ || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
+ return new ProvisioningManager(subId);
+ }
+
+ private ProvisioningManager(int subId) {
+ mSubId = subId;
+ }
+
+ /**
+ * Register a new {@link Callback} to listen to changes to changes in
+ * IMS provisioning. Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+ * Subscription changed events and call
+ * {@link #unregisterProvisioningChangedCallback(Callback)} to clean up after a
+ * subscription is removed.
+ * @param executor The {@link Executor} to call the callback methods on
+ * @param callback The provisioning callbackto be registered.
+ * @see #unregisterProvisioningChangedCallback(Callback)
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerProvisioningChangedCallback(@CallbackExecutor Executor executor,
+ @NonNull Callback callback) {
+ callback.setExecutor(executor);
+ try {
+ getITelephony().registerImsProvisioningChangedCallback(mSubId,
+ callback.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Unregister an existing {@link Callback}. Ensure to call this method when cleaning
+ * up to avoid memory leaks or when the subscription is removed.
+ * @param callback The existing {@link Callback} to be removed.
+ * @see #registerProvisioningChangedCallback(Executor, Callback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterProvisioningChangedCallback(@NonNull Callback callback) {
+ try {
+ getITelephony().unregisterImsProvisioningChangedCallback(mSubId,
+ callback.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query for the integer value associated with the provided key.
+ * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * @return an integer value for the provided key.
+ * @throws IllegalArgumentException if the key provided was invalid.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getProvisioningIntValue(int key) {
+ try {
+ return getITelephony().getImsProvisioningInt(mSubId, key);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query for the String value associated with the provided key.
+ * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * @return a String value for the provided key, or {@code null} if the key doesn't exist.
+ * @throws IllegalArgumentException if the key provided was invalid.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public String getProvisioningStringValue(int key) {
+ try {
+ return getITelephony().getImsProvisioningString(mSubId, key);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the integer value associated with the provided key.
+ * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * @param value a integer value for the provided key.
+ * @return the result of setting the configuration value.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) {
+ try {
+ return getITelephony().setImsProvisioningInt(mSubId, key, value);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the String value associated with the provided key.
+ *
+ * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * @param value a String value for the provided key.
+ * @return the result of setting the configuration value.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key,
+ String value) {
+ try {
+ return getITelephony().setImsProvisioningString(mSubId, key, value);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ private static SubscriptionManager getSubscriptionManager(Context context) {
+ SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
+ if (manager == null) {
+ throw new RuntimeException("Could not find SubscriptionManager.");
+ }
+ return manager;
+ }
+
+ private static ITelephony getITelephony() {
+ ITelephony binder = ITelephony.Stub.asInterface(
+ ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ if (binder == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+ return binder;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index dcd7ea7..321bfff 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -16,9 +16,9 @@
package android.telephony.ims.stub;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.content.Context;
-import android.content.Intent;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.ims.aidl.IImsConfig;
@@ -28,6 +28,8 @@
import com.android.ims.ImsConfig;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.HashMap;
@@ -215,41 +217,6 @@
}
/**
- * Callback that the framework uses for receiving Configuration change updates.
- * {@hide}
- */
- public static class Callback extends IImsConfigCallback.Stub {
-
- @Override
- public final void onIntConfigChanged(int item, int value) throws RemoteException {
- onConfigChanged(item, value);
- }
-
- @Override
- public final void onStringConfigChanged(int item, String value) throws RemoteException {
- onConfigChanged(item, value);
- }
-
- /**
- * Called when the IMS configuration has changed.
- * @param item the IMS configuration key constant, as defined in ImsConfig.
- * @param value the new integer value of the IMS configuration constant.
- */
- public void onConfigChanged(int item, int value) {
- // Base Implementation
- }
-
- /**
- * Called when the IMS configuration has changed.
- * @param item the IMS configuration key constant, as defined in ImsConfig.
- * @param value the new String value of the IMS configuration constant.
- */
- public void onConfigChanged(int item, String value) {
- // Base Implementation
- }
- }
-
- /**
* The configuration requested resulted in an unknown result. This may happen if the
* IMS configurations are unavailable.
*/
@@ -263,6 +230,16 @@
*/
public static final int CONFIG_RESULT_FAILED = 1;
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CONFIG_RESULT_", value = {
+ CONFIG_RESULT_SUCCESS,
+ CONFIG_RESULT_FAILED
+ })
+ public @interface SetConfigResult {}
+
private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
ImsConfigStub mImsConfigStub;
@@ -279,17 +256,16 @@
}
/**
- * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
- * changes.
+ * Adds a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
+ * notified when a value in the configuration changes.
* @param c callback to add.
*/
private void addImsConfigCallback(IImsConfigCallback c) {
mCallbacks.register(c);
}
/**
- * Removes a {@link Callback} to the list of callbacks notified when a value in the
- * configuration changes.
- *
+ * Removes a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
+ * notified when a value in the configuration changes.
* @param c callback to remove.
*/
private void removeImsConfigCallback(IImsConfigCallback c) {
@@ -370,10 +346,9 @@
*
* @param item an integer key.
* @param value an integer containing the configuration value.
- * @return the result of setting the configuration value, defined as either
- * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+ * @return the result of setting the configuration value.
*/
- public int setConfig(int item, int value) {
+ public @SetConfigResult int setConfig(int item, int value) {
// Base Implementation - To be overridden.
return CONFIG_RESULT_FAILED;
}
@@ -383,10 +358,9 @@
*
* @param item an integer key.
* @param value a String containing the new configuration value.
- * @return Result of setting the configuration value, defined as either
- * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+ * @return Result of setting the configuration value.
*/
- public int setConfig(int item, String value) {
+ public @SetConfigResult int setConfig(int item, String value) {
// Base Implementation - To be overridden.
return CONFIG_RESULT_FAILED;
}
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 90e9880..71a2174 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -16,12 +16,17 @@
package com.android.ims;
-import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
import android.os.RemoteException;
import android.telephony.Rlog;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.aidl.IImsConfig;
-import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.aidl.IImsConfigCallback;
+
+import java.util.concurrent.Executor;
/**
* Provides APIs to get/set the IMS service feature/capability/parameters.
@@ -29,8 +34,10 @@
* 1) Items provisioned by the operator.
* 2) Items configured by user. Mainly service feature class.
*
+ * @deprecated Use {@link ProvisioningManager} to change these configurations in the ImsService.
* @hide
*/
+@Deprecated
public class ImsConfig {
private static final String TAG = "ImsConfig";
private boolean DBG = true;
@@ -46,7 +53,7 @@
/**
* Broadcast action: the configuration was changed
- * @deprecated Use {@link ImsConfig#addConfigCallback(ImsConfigImplBase.Callback)} instead.
+ * @deprecated Use {@link android.telephony.ims.ProvisioningManager.Callback} instead.
* @hide
*/
public static final String ACTION_IMS_CONFIG_CHANGED =
@@ -673,13 +680,25 @@
}
/**
- * Adds a {@link ImsConfigImplBase.Callback} to the ImsService to notify when a Configuration
+ * Adds a {@link ProvisioningManager.Callback} to the ImsService to notify when a Configuration
* item has changed.
*
- * Make sure to call {@link #removeConfigCallback(ImsConfigImplBase.Callback)} when finished
+ * Make sure to call {@link #removeConfigCallback(IImsConfigCallback)} when finished
* using this callback.
*/
- public void addConfigCallback(ImsConfigImplBase.Callback callback) throws ImsException {
+ public void addConfigCallback(ProvisioningManager.Callback callback) throws ImsException {
+ callback.setExecutor(getThreadExecutor());
+ addConfigCallback(callback.getBinder());
+ }
+
+ /**
+ * Adds a {@link IImsConfigCallback} to the ImsService to notify when a Configuration
+ * item has changed.
+ *
+ * Make sure to call {@link #removeConfigCallback(IImsConfigCallback)} when finished
+ * using this callback.
+ */
+ public void addConfigCallback(IImsConfigCallback callback) throws ImsException {
if (DBG) Rlog.d(TAG, "addConfigCallback: " + callback);
try {
miConfig.addImsConfigCallback(callback);
@@ -690,10 +709,9 @@
}
/**
- * Removes a {@link ImsConfigImplBase.Callback} from the ImsService that was previously added
- * by {@link #addConfigCallback(ImsConfigImplBase.Callback)}.
+ * Removes an existing {@link IImsConfigCallback} from the ImsService.
*/
- public void removeConfigCallback(ImsConfigImplBase.Callback callback) throws ImsException {
+ public void removeConfigCallback(IImsConfigCallback callback) throws ImsException {
if (DBG) Rlog.d(TAG, "removeConfigCallback: " + callback);
try {
miConfig.removeImsConfigCallback(callback);
@@ -709,4 +727,11 @@
public boolean isBinderAlive() {
return miConfig.asBinder().isBinderAlive();
}
+
+ private Executor getThreadExecutor() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ return new HandlerExecutor(new Handler(Looper.myLooper()));
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3aaa323..85a9cf5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -40,6 +40,7 @@
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
@@ -1569,24 +1570,24 @@
/**
* Adds an IMS registration status callback for the subscription id specified.
*/
- oneway void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+ void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
String callingPackage);
/**
* Removes an existing IMS registration status callback for the subscription specified.
*/
- oneway void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+ void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
String callingPackage);
/**
* Adds an IMS MmTel capabilities callback for the subscription specified.
*/
- oneway void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+ void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
String callingPackage);
/**
* Removes an existing IMS MmTel capabilities callback for the subscription specified.
*/
- oneway void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+ void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
String callingPackage);
/**
@@ -1691,4 +1692,34 @@
* Return a list of certs in hex string from loaded carrier privileges access rules.
*/
List<String> getCertsFromCarrierPrivilegeAccessRules(int subId);
+
+ /**
+ * Register an IMS provisioning change callback with Telephony.
+ */
+ void registerImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
+
+ /**
+ * unregister an existing IMS provisioning change callback.
+ */
+ void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
+
+ /**
+ * Return an integer containing the provisioning value for the specified provisioning key.
+ */
+ int getImsProvisioningInt(int subId, int key);
+
+ /**
+ * return a String containing the provisioning value for the provisioning key specified.
+ */
+ String getImsProvisioningString(int subId, int key);
+
+ /**
+ * Set the integer provisioning value for the provisioning key specified.
+ */
+ int setImsProvisioningInt(int subId, int key, int value);
+
+ /**
+ * Set the String provisioning value for the provisioning key specified.
+ */
+ int setImsProvisioningString(int subId, int key, String value);
}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1fd68ec..7ca3c53 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -61,9 +61,7 @@
ParceledListSlice getPrivilegedConfiguredNetworks();
- WifiConfiguration getMatchingWifiConfig(in ScanResult scanResult);
-
- List<WifiConfiguration> getAllMatchingWifiConfigs(in ScanResult scanResult);
+ List<WifiConfiguration> getAllMatchingWifiConfigs(in List<ScanResult> scanResult);
List<OsuProvider> getMatchingOsuProviders(in ScanResult scanResult);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 954b51f..4b6f5fa 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1073,42 +1073,27 @@
}
/**
- * Returns a WifiConfiguration matching this ScanResult
- *
- * @param scanResult scanResult that represents the BSSID
- * @return {@link WifiConfiguration} that matches this BSSID or null
- * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
- * @hide
- */
- @UnsupportedAppUsage
- public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
- try {
- return mService.getMatchingWifiConfig(scanResult);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Return all matching WifiConfigurations for this ScanResult.
+ * Returns all matching WifiConfigurations for a given list of ScanResult.
*
* An empty list will be returned when no configurations are installed or if no configurations
* match the ScanResult.
- *
- * @param scanResult scanResult that represents the BSSID
- * @return A list of {@link WifiConfiguration}
+
+ * @param scanResults a list of scanResult that represents the BSSID
+ * @return A list of {@link WifiConfiguration} that can have duplicate entries.
* @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @hide
*/
- public List<WifiConfiguration> getAllMatchingWifiConfigs(ScanResult scanResult) {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public List<WifiConfiguration> getAllMatchingWifiConfigs(
+ @NonNull List<ScanResult> scanResults) {
try {
- return mService.getAllMatchingWifiConfigs(scanResult);
+ return mService.getAllMatchingWifiConfigs(scanResults);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
-
/**
* Returns a list of Hotspot 2.0 OSU (Online Sign-Up) providers associated with the given AP.
*
@@ -1119,6 +1104,7 @@
* @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
try {
return mService.getMatchingOsuProviders(scanResult);
@@ -4256,27 +4242,21 @@
/**
* @return true if this device supports WPA3-Personal SAE
- * @hide
*/
- @SystemApi
public boolean isWpa3SaeSupported() {
return isFeatureSupported(WIFI_FEATURE_WPA3_SAE);
}
/**
* @return true if this device supports WPA3-Enterprise Suite-B-192
- * @hide
*/
- @SystemApi
public boolean isWpa3SuiteBSupported() {
return isFeatureSupported(WIFI_FEATURE_WPA3_SUITE_B);
}
/**
* @return true if this device supports Wi-Fi Enhanced Open (OWE)
- * @hide
*/
- @SystemApi
public boolean isOweSupported() {
return isFeatureSupported(WIFI_FEATURE_OWE);
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index e6892be..f58a006 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -491,6 +491,17 @@
/** @hide */
public static final int FACTORY_RESET_SUCCEEDED = BASE + 84;
+ /** @hide */
+ public static final int REQUEST_ONGOING_PEER_CONFIG = BASE + 85;
+ /** @hide */
+ public static final int RESPONSE_ONGOING_PEER_CONFIG = BASE + 86;
+ /** @hide */
+ public static final int SET_ONGOING_PEER_CONFIG = BASE + 87;
+ /** @hide */
+ public static final int SET_ONGOING_PEER_CONFIG_FAILED = BASE + 88;
+ /** @hide */
+ public static final int SET_ONGOING_PEER_CONFIG_SUCCEEDED = BASE + 89;
+
/**
* Create a new WifiP2pManager instance. Applications use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -680,6 +691,18 @@
}
/**
+ * Interface for callback invocation when ongoing peer info is available
+ * @hide
+ */
+ public interface OngoingPeerInfoListener {
+ /**
+ * The requested ongoing WifiP2pConfig is available
+ * @param peerConfig WifiP2pConfig for current connecting session
+ */
+ void onOngoingPeerAvailable(WifiP2pConfig peerConfig);
+ }
+
+ /**
* A channel that connects the application to the Wifi p2p framework.
* Most p2p operations require a Channel as an argument. An instance of Channel is obtained
* by doing a call on {@link #initialize}
@@ -787,6 +810,7 @@
case SET_CHANNEL_FAILED:
case REPORT_NFC_HANDOVER_FAILED:
case FACTORY_RESET_FAILED:
+ case SET_ONGOING_PEER_CONFIG_FAILED:
if (listener != null) {
((ActionListener) listener).onFailure(message.arg1);
}
@@ -814,6 +838,7 @@
case SET_CHANNEL_SUCCEEDED:
case REPORT_NFC_HANDOVER_SUCCEEDED:
case FACTORY_RESET_SUCCEEDED:
+ case SET_ONGOING_PEER_CONFIG_SUCCEEDED:
if (listener != null) {
((ActionListener) listener).onSuccess();
}
@@ -857,6 +882,13 @@
.onHandoverMessageAvailable(handoverMessage);
}
break;
+ case RESPONSE_ONGOING_PEER_CONFIG:
+ WifiP2pConfig peerConfig = (WifiP2pConfig) message.obj;
+ if (listener != null) {
+ ((OngoingPeerInfoListener) listener)
+ .onOngoingPeerAvailable(peerConfig);
+ }
+ break;
default:
Log.d(TAG, "Ignored " + message);
break;
@@ -1536,6 +1568,7 @@
/**
* Removes all saved p2p groups.
+ *
* @param c is the channel created at {@link #initialize}.
* @param listener for callback on success or failure. Can be null.
* @hide
@@ -1550,4 +1583,37 @@
callingPackage);
}
+ /**
+ * Request saved WifiP2pConfig which used for an ongoing peer connection
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener for callback when ongoing peer config updated. Can't be null.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ public void requestOngoingPeerConfig(@NonNull Channel c,
+ @NonNull OngoingPeerInfoListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(REQUEST_ONGOING_PEER_CONFIG,
+ Binder.getCallingUid(), c.putListener(listener));
+ }
+
+ /**
+ * Set saved WifiP2pConfig which used for an ongoing peer connection
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param config used for change an ongoing peer connection
+ * @param listener for callback when ongoing peer config updated. Can be null.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ public void setOngoingPeerConfig(@NonNull Channel c, @NonNull WifiP2pConfig config,
+ @Nullable ActionListener listener) {
+ checkChannel(c);
+ checkP2pConfig(config);
+ c.mAsyncChannel.sendMessage(SET_ONGOING_PEER_CONFIG, 0,
+ c.putListener(listener), config);
+ }
}
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index eede23b..6cdef50 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -25,7 +25,6 @@
import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.IWifiManager;
-import android.net.wifi.PasspointManagementObjectDefinition;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiConfiguration;
@@ -37,7 +36,6 @@
import android.os.Messenger;
import android.os.ResultReceiver;
import android.os.WorkSource;
-import android.util.Slog;
import java.util.List;
@@ -83,17 +81,34 @@
throw new UnsupportedOperationException();
}
- @Override
+ /**
+ * Returns a WifiConfiguration matching this ScanResult
+ * @param scanResult a single ScanResult Object
+ * @return
+ * @deprecated use {@link #getAllMatchingWifiConfigs(List)} instead.
+ */
+ @Deprecated
public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
throw new UnsupportedOperationException();
}
- @Override
+ /**
+ * Returns all matching WifiConfigurations for this ScanResult.
+ * @param scanResult a single ScanResult Object
+ * @return
+ * @deprecated use {@link #getAllMatchingWifiConfigs(List)} instead.
+ */
+ @Deprecated
public List<WifiConfiguration> getAllMatchingWifiConfigs(ScanResult scanResult) {
throw new UnsupportedOperationException();
}
@Override
+ public List<WifiConfiguration> getAllMatchingWifiConfigs(List<ScanResult> scanResults) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index ea41bb3..ecf65c9 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -35,7 +35,20 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyList;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -62,6 +75,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.List;
/**
* Unit tests for {@link android.net.wifi.WifiManager}.
@@ -1250,4 +1264,26 @@
userSelectionCallbackCaptor.getValue().reject();
verify(iUserSelectionCallback).reject();
}
+
+ /**
+ * Check the call to getAllMatchingWifiConfigs calls getAllMatchingWifiConfigs of WifiService
+ * with the provided a list of ScanResult.
+ */
+ @Test
+ public void testGetAllMatchingWifiConfigs() throws Exception {
+ mWifiManager.getAllMatchingWifiConfigs(new ArrayList<>());
+
+ verify(mWifiService).getAllMatchingWifiConfigs(any(List.class));
+ }
+
+ /**
+ * Check the call to getMatchingOsuProviders calls getMatchingOsuProviders of WifiService
+ * with the provided a single ScanResult.
+ */
+ @Test
+ public void testGetMatchingOsuProviders() throws Exception {
+ mWifiManager.getMatchingOsuProviders(new ScanResult());
+
+ verify(mWifiService).getMatchingOsuProviders(any(ScanResult.class));
+ }
}