Merge branch 'security-aosp-pi-release' into int/p/fp2
* security-aosp-pi-release:
Force-set a ClipData to prevent later migration.
Revert "Revert "[pm] remove old stage dirs on low storage""
Prevent apps from spamming addAccountExplicitly. See comment here for the discussion on solution https://b.corp.google.com/issues/169762606#comment14
Import translations. DO NOT MERGE ANYWHERE
Fix serialization bug in GpsNavigationMessage
Change-Id: I23958a578407c2cf84c75f6859fc460b452faa72
diff --git a/Android.bp b/Android.bp
index 22fe23d..3a7a575 100644
--- a/Android.bp
+++ b/Android.bp
@@ -584,6 +584,8 @@
"packages/services/Proxy/com/android/net/IProxyPortListener.aidl",
"core/java/android/service/quicksettings/IQSService.aidl",
"core/java/android/service/quicksettings/IQSTileService.aidl",
+ "telephony/java/com/android/internal/telephony/ISmsSecurityService.aidl",
+ "telephony/java/com/android/internal/telephony/ISmsSecurityAgent.aidl",
":libupdate_engine_aidl",
diff --git a/api/current.txt b/api/current.txt
index 557d536..87ac40b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4215,6 +4215,7 @@
method public int noteOpNoThrow(java.lang.String, int, java.lang.String);
method public int noteProxyOp(java.lang.String, java.lang.String);
method public int noteProxyOpNoThrow(java.lang.String, java.lang.String);
+ method public static int opToDefaultMode(int);
method public static java.lang.String permissionToOp(java.lang.String);
method public int startOp(java.lang.String, int, java.lang.String);
method public int startOpNoThrow(java.lang.String, int, java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index e34614e..2bd60b8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -248,8 +248,8 @@
public abstract class PackageManager {
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public abstract int getInstallReason(java.lang.String, android.os.UserHandle);
- method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
+ method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
method public abstract java.lang.String[] getNamesForUids(int[]);
method public abstract java.lang.String getPermissionControllerPackageName();
method public abstract java.lang.String getServicesSystemSharedLibraryPackageName();
diff --git a/config/hiddenapi-private-dex.txt b/config/hiddenapi-private-dex.txt
index 2c48655..f61c1a5 100644
--- a/config/hiddenapi-private-dex.txt
+++ b/config/hiddenapi-private-dex.txt
@@ -4421,9 +4421,9 @@
Landroid/app/AppOpsManager$OnOpChangedInternalListener;-><init>()V
Landroid/app/AppOpsManager$OnOpChangedInternalListener;->onOpChanged(ILjava/lang/String;)V
Landroid/app/AppOpsManager$OpEntry;
-Landroid/app/AppOpsManager$OpEntry;-><init>(IIJJIILjava/lang/String;)V
-Landroid/app/AppOpsManager$OpEntry;-><init>(II[J[JIILjava/lang/String;)V
-Landroid/app/AppOpsManager$OpEntry;-><init>(II[J[JIZILjava/lang/String;)V
+Landroid/app/AppOpsManager$OpEntry;-><init>(IIJJIILjava/lang/String;II)V
+Landroid/app/AppOpsManager$OpEntry;-><init>(II[J[JIILjava/lang/String;II)V
+Landroid/app/AppOpsManager$OpEntry;-><init>(II[J[JIZILjava/lang/String;II)V
Landroid/app/AppOpsManager$OpEntry;-><init>(Landroid/os/Parcel;)V
Landroid/app/AppOpsManager$OpEntry;->CREATOR:Landroid/os/Parcelable$Creator;
Landroid/app/AppOpsManager$OpEntry;->getDuration()I
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c58b91e..a112caf 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012 The Android Open Source Project
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -119,6 +120,12 @@
public static final int MODE_FOREGROUND = 4;
/**
+ * @hide Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}:
+ * AppOps Service should show a dialog box on screen to get user permission.
+ */
+ public static final int MODE_ASK = 5;
+
+ /**
* Flag for {@link #startWatchingMode(String, String, int, OnOpChangedListener)}:
* Also get reports if the foreground state of an op's uid changes. This only works
* when watching a particular op, not when watching a package.
@@ -135,6 +142,7 @@
"deny", // MODE_ERRORED
"default", // MODE_DEFAULT
"foreground", // MODE_FOREGROUND
+ "ask", // MODE_ASK
};
/**
@@ -188,7 +196,8 @@
// when adding one of these:
// - increment _NUM_OP
// - define an OPSTR_* constant (marked as @SystemApi)
- // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpToPerms, sOpDefault
+ // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpToPerms, sOpDefault,
+ // sOpDefaultStrictMode, sOpToOpString, sOpStrictMode.
// - add descriptive strings to Settings/res/values/arrays.xml
// - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
@@ -352,8 +361,18 @@
public static final int OP_START_FOREGROUND = 76;
/** @hide */
public static final int OP_BLUETOOTH_SCAN = 77;
+ /** @hide Bluetooth state change */
+ public static final int OP_BLUETOOTH_CHANGE = 78;
+ /** @hide Boot completed */
+ public static final int OP_BOOT_COMPLETED = 79;
+ /** @hide NFC state change */
+ public static final int OP_NFC_CHANGE = 80;
+ /** @hide Data connect state change */
+ public static final int OP_DATA_CONNECT_CHANGE = 81;
+ /** @hide SU access */
+ public static final int OP_SU = 82;
/** @hide */
- public static final int _NUM_OP = 78;
+ public static final int _NUM_OP = 83;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -597,6 +616,17 @@
public static final String OPSTR_START_FOREGROUND = "android:start_foreground";
/** @hide */
public static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan";
+ /** @hide */
+ public static final String OPSTR_BLUETOOTH_CHANGE = "android:bluetooth_change";
+ /** @hide */
+ public static final String OPSTR_BOOT_COMPLETED = "android:boot_completed";
+ /** @hide */
+ public static final String OPSTR_NFC_CHANGE = "android:nfc_change";
+ /** @hide */
+ public static final String OPSTR_DATA_CONNECT_CHANGE = "android:data_connect_change";
+
+ /** @hide */
+ public static final String OPSTR_SU = "android:su";
// Warning: If an permission is added here it also has to be added to
// com.android.packageinstaller.permission.utils.EventLogger
@@ -646,6 +676,7 @@
OP_WRITE_SETTINGS,
OP_REQUEST_INSTALL_PACKAGES,
OP_START_FOREGROUND,
+ OP_SU
};
/**
@@ -667,7 +698,7 @@
OP_WRITE_CALL_LOG, // WRITE_CALL_LOG
OP_READ_CALENDAR, // READ_CALENDAR
OP_WRITE_CALENDAR, // WRITE_CALENDAR
- OP_COARSE_LOCATION, // WIFI_SCAN
+ OP_WIFI_SCAN, // WIFI_SCAN
OP_POST_NOTIFICATION, // POST_NOTIFICATION
OP_COARSE_LOCATION, // NEIGHBORING_CELLS
OP_CALL_PHONE, // CALL_PHONE
@@ -734,7 +765,12 @@
OP_ACCEPT_HANDOVER, // ACCEPT_HANDOVER
OP_MANAGE_IPSEC_TUNNELS, // MANAGE_IPSEC_HANDOVERS
OP_START_FOREGROUND, // START_FOREGROUND
- OP_COARSE_LOCATION, // BLUETOOTH_SCAN
+ OP_BLUETOOTH_SCAN, // BLUETOOTH_SCAN
+ OP_BLUETOOTH_CHANGE, // BLUETOOTH_CHANGE
+ OP_BOOT_COMPLETED, // BOOT_COMPLETED
+ OP_NFC_CHANGE, // NFC_CHANGE
+ OP_DATA_CONNECT_CHANGE, // DATA_CONNECT_CHANGE
+ OP_SU, // SU
};
/**
@@ -819,6 +855,11 @@
OPSTR_MANAGE_IPSEC_TUNNELS,
OPSTR_START_FOREGROUND,
OPSTR_BLUETOOTH_SCAN,
+ OPSTR_BLUETOOTH_CHANGE,
+ OPSTR_BOOT_COMPLETED,
+ OPSTR_NFC_CHANGE,
+ OPSTR_DATA_CONNECT_CHANGE,
+ OPSTR_SU,
};
/**
@@ -904,6 +945,11 @@
"MANAGE_IPSEC_TUNNELS",
"START_FOREGROUND",
"BLUETOOTH_SCAN",
+ "BLUETOOTH_CHANGE",
+ "BOOT_COMPLETED",
+ "NFC_CHANGE",
+ "DATA_CONNECT_CHANGE",
+ "SU",
};
/**
@@ -921,7 +967,7 @@
android.Manifest.permission.WRITE_CALL_LOG,
android.Manifest.permission.READ_CALENDAR,
android.Manifest.permission.WRITE_CALENDAR,
- android.Manifest.permission.ACCESS_WIFI_STATE,
+ null, // no permission for wifi scan available
null, // no permission required for notifications
null, // neighboring cells shares the coarse location perm
android.Manifest.permission.CALL_PHONE,
@@ -989,6 +1035,11 @@
null, // no permission for OP_MANAGE_IPSEC_TUNNELS
Manifest.permission.FOREGROUND_SERVICE,
null, // no permission for OP_BLUETOOTH_SCAN
+ null,
+ Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ Manifest.permission.NFC,
+ null,
+ null, // no permission for OP_SU
};
/**
@@ -1075,6 +1126,11 @@
null, // MANAGE_IPSEC_TUNNELS
null, // START_FOREGROUND
null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN
+ null, // BLUETOOTH_CHANGE
+ null, // BOOT_COMPLETED
+ null, // NFC_CHANGE
+ null, // DATA_CONNECT_CHANGE
+ UserManager.DISALLOW_SU, // SU TODO: this should really be investigated.
};
/**
@@ -1160,6 +1216,11 @@
false, // MANAGE_IPSEC_HANDOVERS
false, // START_FOREGROUND
true, // BLUETOOTH_SCAN
+ true, // BLUETOOTH_CHANGE
+ true, // BOOT_COMPLETED
+ true, // NFC_CHANGE
+ true, // DATA_CONNECT_CHANGE
+ false, // SU
};
/**
@@ -1244,6 +1305,190 @@
AppOpsManager.MODE_ERRORED, // MANAGE_IPSEC_TUNNELS
AppOpsManager.MODE_ALLOWED, // OP_START_FOREGROUND
AppOpsManager.MODE_ALLOWED, // OP_BLUETOOTH_SCAN
+ AppOpsManager.MODE_ALLOWED, // OP_BLUETOOTH_CHANGE
+ AppOpsManager.MODE_ALLOWED, // OP_BOOT_COMPLETED
+ AppOpsManager.MODE_ALLOWED, // OP_NFC_CHANGE
+ AppOpsManager.MODE_ALLOWED, // OP_DATA_CONNECT_CHANGE
+ AppOpsManager.MODE_ASK, // OP_SU
+ };
+
+ /**
+ * This specifies the default mode for each strict operation.
+ */
+
+ private static int[] sOpDefaultStrictMode = new int[] {
+ AppOpsManager.MODE_ASK, // OP_COARSE_LOCATION
+ AppOpsManager.MODE_ASK, // OP_FINE_LOCATION
+ AppOpsManager.MODE_ASK, // OP_GPS
+ AppOpsManager.MODE_ALLOWED, // OP_VIBRATE
+ AppOpsManager.MODE_ASK, // OP_READ_CONTACTS
+ AppOpsManager.MODE_ASK, // OP_WRITE_CONTACTS
+ AppOpsManager.MODE_ASK, // OP_READ_CALL_LOG
+ AppOpsManager.MODE_ASK, // OP_WRITE_CALL_LOG
+ AppOpsManager.MODE_ALLOWED, // OP_READ_CALENDAR
+ AppOpsManager.MODE_ALLOWED, // OP_WRITE_CALENDAR
+ AppOpsManager.MODE_ASK, // OP_WIFI_SCAN
+ AppOpsManager.MODE_ALLOWED, // OP_POST_NOTIFICATION
+ AppOpsManager.MODE_ALLOWED, // OP_NEIGHBORING_CELLS
+ AppOpsManager.MODE_ASK, // OP_CALL_PHONE
+ AppOpsManager.MODE_ASK, // OP_READ_SMS
+ AppOpsManager.MODE_ASK, // OP_WRITE_SMS
+ AppOpsManager.MODE_ASK, // OP_RECEIVE_SMS
+ AppOpsManager.MODE_ALLOWED, // OP_RECEIVE_EMERGECY_SMS
+ AppOpsManager.MODE_ASK, // OP_RECEIVE_MMS
+ AppOpsManager.MODE_ALLOWED, // OP_RECEIVE_WAP_PUSH
+ AppOpsManager.MODE_ASK, // OP_SEND_SMS
+ AppOpsManager.MODE_ALLOWED, // OP_READ_ICC_SMS
+ AppOpsManager.MODE_ALLOWED, // OP_WRITE_ICC_SMS
+ AppOpsManager.MODE_ALLOWED, // OP_WRITE_SETTINGS
+ AppOpsManager.MODE_ALLOWED, // OP_SYSTEM_ALERT_WINDOW
+ AppOpsManager.MODE_ALLOWED, // OP_ACCESS_NOTIFICATIONS
+ AppOpsManager.MODE_ASK, // OP_CAMERA
+ AppOpsManager.MODE_ASK, // OP_RECORD_AUDIO
+ AppOpsManager.MODE_ALLOWED, // OP_PLAY_AUDIO
+ AppOpsManager.MODE_ALLOWED, // OP_READ_CLIPBOARD
+ AppOpsManager.MODE_ALLOWED, // OP_WRITE_CLIPBOARD
+ AppOpsManager.MODE_ALLOWED, // OP_TAKE_MEDIA_BUTTONS
+ AppOpsManager.MODE_ALLOWED, // OP_TAKE_AUDIO_FOCUS
+ AppOpsManager.MODE_ALLOWED, // OP_AUDIO_MASTER_VOLUME
+ AppOpsManager.MODE_ALLOWED, // OP_AUDIO_VOICE_VOLUME
+ AppOpsManager.MODE_ALLOWED, // OP_AUDIO_RING_VOLUME
+ AppOpsManager.MODE_ALLOWED, // OP_AUDIO_MEDIA_VOLUME
+ AppOpsManager.MODE_ALLOWED, // OP_AUDIO_ALARM_VOLUME
+ AppOpsManager.MODE_ALLOWED, // OP_AUDIO_NOTIFICATION_VOLUME
+ AppOpsManager.MODE_ALLOWED, // OP_AUDIO_BLUETOOTH_VOLUME
+ AppOpsManager.MODE_ALLOWED, // OP_WAKE_LOCK
+ AppOpsManager.MODE_ALLOWED, // OP_MONITOR_LOCATION
+ AppOpsManager.MODE_ASK, // OP_MONITOR_HIGH_POWER_LOCATION
+ AppOpsManager.MODE_DEFAULT, // OP_GET_USAGE_STATS
+ AppOpsManager.MODE_ALLOWED, // OP_MUTE_MICROPHONE
+ AppOpsManager.MODE_ALLOWED, // OP_TOAST_WINDOW
+ AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
+ AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN
+ AppOpsManager.MODE_ALLOWED, // OP WALLPAPER
+ AppOpsManager.MODE_ALLOWED, // OP_ASSIST_STRUCTURE
+ AppOpsManager.MODE_ALLOWED, // OP_ASSIST_SCREENSHOT
+ AppOpsManager.MODE_ALLOWED, // OP_READ_PHONE_STATE
+ AppOpsManager.MODE_ALLOWED, // OP_ADD_VOICEMAIL
+ AppOpsManager.MODE_ALLOWED, // OP_USE_SIP
+ AppOpsManager.MODE_ALLOWED, // OP_PROCESS_OUTGOING_CALLS
+ AppOpsManager.MODE_ALLOWED, // OP_USE_FINGERPRINT
+ AppOpsManager.MODE_ALLOWED, // OP_BODY_SENSORS
+ AppOpsManager.MODE_ALLOWED, // OP_READ_CELL_BROADCASTS
+ AppOpsManager.MODE_ERRORED, // OP_MOCK_LOCATION
+ AppOpsManager.MODE_ALLOWED, // OP_READ_EXTERNAL_STORAGE
+ AppOpsManager.MODE_ALLOWED, // OP_WRITE_EXTERNAL_STORAGE
+ AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN
+ AppOpsManager.MODE_ALLOWED, // OP_GET_ACCOUNTS
+ AppOpsManager.MODE_ASK, // MODE_RUN_IN_BACKGROUND
+ AppOpsManager.MODE_ALLOWED, // OP_AUDIO_ACCESSIBILITY_VOLUME
+ AppOpsManager.MODE_ALLOWED, // READ_PHONE_NUMBERS
+ AppOpsManager.MODE_DEFAULT, // OP_REQUEST_INSTALL_PACKAGES
+ AppOpsManager.MODE_ALLOWED, // OP_PICTURE_IN_PICTURE
+ AppOpsManager.MODE_DEFAULT, // OP_INSTANT_APP_START_FOREGROUND
+ AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS
+ AppOpsManager.MODE_ASK, // OP_RUN_ANY_IN_BACKGROUND
+ AppOpsManager.MODE_ALLOWED, // OP_CHANGE_WIFI_STATE
+ AppOpsManager.MODE_ALLOWED, // REQUEST_DELETE_PACKAGES
+ AppOpsManager.MODE_ALLOWED, // OP_BIND_ACCESSIBILITY_SERVICE
+ AppOpsManager.MODE_ALLOWED, // ACCEPT_HANDOVER
+ AppOpsManager.MODE_ERRORED, // MANAGE_IPSEC_TUNNELS
+ AppOpsManager.MODE_ALLOWED, // OP_START_FOREGROUND
+ AppOpsManager.MODE_ALLOWED, // OP_BLUETOOTH_SCAN
+ AppOpsManager.MODE_ASK, // OP_BLUETOOTH_CHANGE
+ AppOpsManager.MODE_ASK, // OP_BOOT_COMPLETED
+ AppOpsManager.MODE_ASK, // OP_NFC_CHANGE
+ AppOpsManager.MODE_ASK, // OP_DATA_CONNECT_CHANGE
+ AppOpsManager.MODE_ASK, // OP_SU
+ };
+
+ /**
+ * This specifies if operation is in strict mode.
+ */
+ private final static boolean[] sOpStrictMode = new boolean[] {
+ true, // COARSE_LOCATION
+ true, // FINE_LOCATION
+ true, // GPS
+ false, // VIBRATE
+ true, // READ_CONTACTS
+ true, // WRITE_CONTACTS
+ true, // READ_CALL_LOG
+ true, // WRITE_CALL_LOG
+ false, // READ_CALENDAR
+ false, // WRITE_CALENDAR
+ true, // WIFI_SCAN
+ false, // POST_NOTIFICATION
+ false, // NEIGHBORING_CELLS
+ true, // CALL_PHONE
+ true, // READ_SMS
+ true, // WRITE_SMS
+ true, // RECEIVE_SMS
+ false, // RECEIVE_EMERGECY_SMS
+ true, // RECEIVE_MMS
+ false, // RECEIVE_WAP_PUSH
+ true, // SEND_SMS
+ true, // READ_ICC_SMS
+ true, // WRITE_ICC_SMS
+ true, // WRITE_SETTINGS
+ false, // SYSTEM_ALERT_WINDOW
+ false, // ACCESS_NOTIFICATIONS
+ true, // CAMERA
+ true, // RECORD_AUDIO
+ true, // PLAY_AUDIO
+ false, // READ_CLIPBOARD
+ false, // WRITE_CLIPBOARD
+ true, // TAKE_MEDIA_BUTTONS
+ true, // TAKE_AUDIO_FOCUS
+ false, // AUDIO_MASTER_VOLUME
+ false, // AUDIO_VOICE_VOLUME
+ false, // AUDIO_RING_VOLUME
+ false, // AUDIO_MEDIA_VOLUME
+ false, // AUDIO_ALARM_VOLUME
+ false, // AUDIO_NOTIFICATION_VOLUME
+ false, // AUDIO_BLUETOOTH_VOLUME
+ false, // WAKE_LOCK
+ true, // MONITOR_LOCATION
+ true, // MONITOR_HIGH_POWER_LOCATION
+ true, // GET_USAGE_STATS
+ false, // MUTE_MICROPHONE
+ false, // TOAST_WINDOW
+ true, // PROJECT_MEDIA
+ false, // ACTIVATE_VPN
+ false, // WALLPAPER
+ false, // ASSIST_STRUCTURE
+ false, // ASSIST_SCREENSHOT
+ true, // READ_PHONE_STATE
+ true, // ADD_VOICEMAIL
+ true, // USE_SIP
+ true, // PROCESS_OUTGOING_CALLS
+ true, // USE_FINGERPRINT
+ true, // BODY_SENSORS
+ false, // READ_CELL_BROADCASTS
+ true, // MOCK_LOCATION
+ true, // READ_EXTERNAL_STORAGE
+ true, // WRITE_EXTERNAL_STORAGE
+ false, // TURN_ON_SCREEN
+ false, // GET_ACCOUNTS
+ true, // RUN_IN_BACKGROUND
+ false, // AUDIO_ACCESSIBILITY_VOLUME
+ true, // READ_PHONE_NUMBERS
+ true, // REQUEST_INSTALL_PACKAGES
+ true, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
+ true, // INSTANT_APP_START_FOREGROUND
+ false, // ANSWER_PHONE_CALLS
+ true, // OP_RUN_ANY_IN_BACKGROUND
+ false, // OP_CHANGE_WIFI_STATE
+ false, // OP_REQUEST_DELETE_PACKAGES
+ false, // OP_BIND_ACCESSIBILITY_SERVICE
+ false, // ACCEPT_HANDOVER
+ false, // MANAGE_IPSEC_HANDOVERS
+ false, // START_FOREGROUND
+ true, // BLUETOOTH_SCAN
+ true, // BLUETOOTH_CHANGE
+ true, // BOOT_COMPLETED
+ true, // NFC_CHANGE
+ true, // DATA_CONNECT_CHANGE
+ true, // SU
};
/**
@@ -1332,6 +1577,11 @@
false, // MANAGE_IPSEC_TUNNELS
false, // START_FOREGROUND
false, // BLUETOOTH_SCAN
+ false, // OP_BLUETOOTH_CHANGE
+ false, // OP_BOOT_COMPLETED
+ false, // OP_NFC_CHANGE
+ false, // OP_DATA_CONNECT_CHANGE
+ false, // OP_SU
};
/**
@@ -1344,6 +1594,20 @@
*/
private static HashMap<String, Integer> sPermToOp = new HashMap<>();
+ private static HashMap<String, Integer> sNameToOp = new HashMap<String, Integer>();
+
+ /**
+ * App op guard states.
+ * @hide
+ */
+ public static final int[] PRIVACY_GUARD_OP_STATES = new int[] {
+ OP_COARSE_LOCATION,
+ OP_READ_CALL_LOG,
+ OP_READ_CONTACTS,
+ OP_READ_CALENDAR,
+ OP_READ_SMS,
+ };
+
static {
if (sOpToSwitch.length != _NUM_OP) {
throw new IllegalStateException("sOpToSwitch length " + sOpToSwitch.length
@@ -1365,6 +1629,10 @@
throw new IllegalStateException("sOpDefaultMode length " + sOpDefaultMode.length
+ " should be " + _NUM_OP);
}
+ if (sOpDefaultStrictMode.length != _NUM_OP) {
+ throw new IllegalStateException("sOpDefaultStrictMode length "
+ + sOpDefaultStrictMode.length + " should be " + _NUM_OP);
+ }
if (sOpDisableReset.length != _NUM_OP) {
throw new IllegalStateException("sOpDisableReset length " + sOpDisableReset.length
+ " should be " + _NUM_OP);
@@ -1377,6 +1645,10 @@
throw new IllegalStateException("sOpAllowSYstemRestrictionsBypass length "
+ sOpRestrictions.length + " should be " + _NUM_OP);
}
+ if (sOpStrictMode.length != _NUM_OP) {
+ throw new IllegalStateException("sOpStrictMode length " + sOpStrictMode.length
+ + " should be " + _NUM_OP);
+ }
for (int i=0; i<_NUM_OP; i++) {
if (sOpToString[i] != null) {
sOpStrToOp.put(sOpToString[i], i);
@@ -1387,6 +1659,9 @@
sPermToOp.put(sOpPerms[op], op);
}
}
+ for (int i=0; i<_NUM_OP; i++) {
+ sNameToOp.put(sOpNames[i], i);
+ }
}
/**
@@ -1419,6 +1694,15 @@
}
/**
+ * Map a non-localized name for the operation back to the Op number
+ * @hide
+ */
+ public static int nameToOp(String name) {
+ Integer val = sNameToOp.get(name);
+ return val != null ? val : OP_NONE;
+ }
+
+ /**
* Retrieve the permission associated with an operation, or null if there is not one.
* @hide
*/
@@ -1458,10 +1742,17 @@
* Retrieve the default mode for the operation.
* @hide
*/
- public static int opToDefaultMode(int op) {
+ public static int opToDefaultMode(int op, boolean isStrict) {
+ if (isStrict) {
+ return sOpDefaultStrictMode[op];
+ }
return sOpDefaultMode[op];
}
+ public static int opToDefaultMode(int op) {
+ return opToDefaultMode(op, false);
+ }
+
/**
* Retrieve the human readable mode.
* @hide
@@ -1557,9 +1848,11 @@
private final int mProxyUid;
private final boolean mRunning;
private final String mProxyPackageName;
+ private final int mAllowedCount;
+ private final int mIgnoredCount;
public OpEntry(int op, int mode, long time, long rejectTime, int duration,
- int proxyUid, String proxyPackage) {
+ int proxyUid, String proxyPackage, int allowedCount, int ignoredCount) {
mOp = op;
mMode = mode;
mTimes = new long[_NUM_UID_STATE];
@@ -1570,10 +1863,13 @@
mRunning = duration == -1;
mProxyUid = proxyUid;
mProxyPackageName = proxyPackage;
+ mAllowedCount = allowedCount;
+ mIgnoredCount = ignoredCount;
}
public OpEntry(int op, int mode, long[] times, long[] rejectTimes, int duration,
- boolean running, int proxyUid, String proxyPackage) {
+ boolean running, int proxyUid, String proxyPackage,
+ int allowedCount, int ignoredCount) {
mOp = op;
mMode = mode;
mTimes = new long[_NUM_UID_STATE];
@@ -1584,11 +1880,14 @@
mRunning = running;
mProxyUid = proxyUid;
mProxyPackageName = proxyPackage;
+ mAllowedCount = allowedCount;
+ mIgnoredCount = ignoredCount;
}
public OpEntry(int op, int mode, long[] times, long[] rejectTimes, int duration,
- int proxyUid, String proxyPackage) {
- this(op, mode, times, rejectTimes, duration, duration == -1, proxyUid, proxyPackage);
+ int proxyUid, String proxyPackage, int allowedCount, int ignoredCount) {
+ this(op, mode, times, rejectTimes, duration, duration == -1, proxyUid, proxyPackage,
+ allowedCount, ignoredCount);
}
public int getOp() {
@@ -1655,6 +1954,14 @@
return mProxyPackageName;
}
+ public int getAllowedCount() {
+ return mAllowedCount;
+ }
+
+ public int getIgnoredCount() {
+ return mIgnoredCount;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -1670,6 +1977,8 @@
dest.writeBoolean(mRunning);
dest.writeInt(mProxyUid);
dest.writeString(mProxyPackageName);
+ dest.writeInt(mAllowedCount);
+ dest.writeInt(mIgnoredCount);
}
OpEntry(Parcel source) {
@@ -1681,6 +1990,8 @@
mRunning = source.readBoolean();
mProxyUid = source.readInt();
mProxyPackageName = source.readString();
+ mAllowedCount = source.readInt();
+ mIgnoredCount = source.readInt();
}
public static final Creator<OpEntry> CREATOR = new Creator<OpEntry>() {
@@ -2589,4 +2900,37 @@
}
return time;
}
+
+ /**
+ * Check if op in strict mode
+ * @hide
+ */
+ public static boolean isStrictOp(int code) {
+ return sOpStrictMode[code];
+ }
+
+ /** @hide */
+ public boolean getPrivacyGuardSettingForPackage(int uid, String packageName) {
+ try {
+ return mService.getPrivacyGuardSettingForPackage(uid, packageName);
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /** @hide */
+ public void setPrivacyGuardSettingForPackage(int uid, String packageName, boolean state) {
+ try {
+ mService.setPrivacyGuardSettingForPackage(uid, packageName, state);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** @hide */
+ public void resetCounters() {
+ try {
+ mService.resetCounters();
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 9350aab..22e1f45 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -571,9 +571,7 @@
/**
* An empty Camera for testing purpose.
*/
- Camera() {
- initAppOps();
- }
+ Camera() {}
private void initAppOps() {
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 35aaf78..27f2414 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -22,6 +22,7 @@
import android.os.Build;
import android.os.Handler;
import android.os.MemoryFile;
+import android.os.SystemProperties;
import android.util.Log;
import android.util.SparseArray;
@@ -83,6 +84,9 @@
/** @hide */
protected static final String TAG = "SensorManager";
+ /** {@hide} */
+ public static final String PROP_FP2_USE_GYROSCOPE = "persist.fp2.use_gyroscope";
+
private static final float[] sTempMatrix = new float[16];
// Cached lists of sensors by type. Guarded by mSensorListByType.
@@ -1035,6 +1039,15 @@
return sensors.size() > 0;
}
+ /** {@hide}
+ * Tell if the Fairphone 2 gyroscope sensor has been enabled by the user.
+ *
+ * @return <code>true/code> if the gyroscope sensor is enabled, <code>false</code> otherwise.
+ */
+ public static boolean isFp2GyroscopeEnabled() {
+ return SystemProperties.getBoolean(PROP_FP2_USE_GYROSCOPE, false);
+ }
+
/** @hide */
protected abstract void registerDynamicSensorCallbackImpl(
DynamicSensorCallback callback, Handler handler);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b591851..dc00651 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -962,6 +962,19 @@
public static final String DISALLOW_PRINTING = "no_printing";
/**
+ * Specifies whether the user is allowed to use SU commands.
+ *
+ * The default value is <code>false</code>.
+ *
+ * <p/>Key for user restrictions.
+ * <p/>Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ * @hide
+ */
+ public static final String DISALLOW_SU = "no_su";
+
+ /**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5ea5bd7..2933dc4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -899,6 +899,19 @@
/**
* @hide
+ * Activity Action: Show the "app ops" details screen.
+ * <p>
+ * Input: The Intent's data URI specifies the application package name
+ * to be shown, with the "package" scheme. That is "package:com.my.app".
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APP_OPS_DETAILS_SETTINGS =
+ "android.settings.APP_OPS_DETAILS_SETTINGS";
+
+ /**
+ * @hide
* Activity Action: Show the "app ops" settings screen.
* <p>
* Input: Nothing.
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 0ed9724..c2a1a34 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -56,4 +56,11 @@
boolean isOperationActive(int code, int uid, String packageName);
void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback);
+
+ // Privacy guard methods
+ boolean getPrivacyGuardSettingForPackage(int uid, String packageName);
+ void setPrivacyGuardSettingForPackage(int uid, String packageName, boolean state);
+
+ // AppOps accounting
+ void resetCounters();
}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 246a50f..5f2040e 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2018-2020 Fairphone B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,9 +18,11 @@
package com.android.internal.os;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
@@ -27,6 +30,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedReader;
+import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -38,6 +43,8 @@
*/
public class PowerProfile {
+ private static final String TAG = "PowerProfile";
+
/*
* POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode.
* POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should
@@ -221,6 +228,9 @@
private static final String TAG_ARRAYITEM = "value";
private static final String ATTR_NAME = "name";
+ private static final String FILENAME_BATTERY_DESIGN_CAPACITY =
+ "/sys/class/power_supply/bms/charge_full_design";
+
private static final Object sLock = new Object();
@VisibleForTesting
@@ -325,6 +335,43 @@
sPowerItemMap.put(key, (double) value);
}
}
+
+ /*
+ * There is no way to set the battery design capacity automatically here from what the
+ * kernel exposes in the sysfs. Several battery packs can be available for the device and
+ * they might have different capacities. So do not rely on the XML power profile and always
+ * use what the kernel exposes instead.
+ */
+ try {
+ // Read the battery design capacity from the sysfs, in microampere-hour
+ int batteryDesignCapacityMicro =
+ Integer.parseInt(readLine(FILENAME_BATTERY_DESIGN_CAPACITY));
+ // Unconditionally store the sysfs capacity, in milliampere-hour
+ sPowerItemMap.put(POWER_BATTERY_CAPACITY, ((double) batteryDesignCapacityMicro) / 1000);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Cannot read battery capacity from "
+ + FILENAME_BATTERY_DESIGN_CAPACITY, ioe);
+ } catch (NumberFormatException nfe) {
+ Log.e(TAG, "Read a badly formatted battery capacity from "
+ + FILENAME_BATTERY_DESIGN_CAPACITY, nfe);
+ }
+ }
+
+ /**
+ * Reads a line from the specified file.
+ *
+ * @param filename The file to read from.
+ * @return The first line up to 256 characters, or <code>null</code> if file is empty.
+ * @throws IOException If the file couldn't be read.
+ */
+ @Nullable
+ private static String readLine(String filename) throws IOException {
+ final BufferedReader reader = new BufferedReader(new FileReader(filename), 256);
+ try {
+ return reader.readLine();
+ } finally {
+ reader.close();
+ }
}
private CpuClusterKey[] mCpuClusters;
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index c5be8e4..f03ed752 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -22,6 +22,7 @@
import android.content.ComponentName;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
+import android.hardware.SensorManager;
import android.os.Build;
import android.os.Environment;
import android.os.Process;
@@ -723,6 +724,11 @@
addFeature(PackageManager.FEATURE_RAM_NORMAL, 0);
}
+ // The Fairphone 2 gyroscope sensor can be toggled by the user
+ if (!SensorManager.isFp2GyroscopeEnabled()) {
+ mUnavailableFeatures.add(PackageManager.FEATURE_SENSOR_GYROSCOPE);
+ }
+
for (String featureName : mUnavailableFeatures) {
removeFeature(featureName);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e81268b..871a6da 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1267,6 +1267,11 @@
<permission android:name="android.permission.MODIFY_CELL_BROADCASTS"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to authorize outgoing SMS messages.
+ @hide -->
+ <permission android:name="com.qti.permission.AUTHORIZE_OUTGOING_SMS"
+ android:protectionLevel="signature" />
+
<!-- =============================================================== -->
<!-- Permissions for setting the device alarm -->
<!-- =============================================================== -->
diff --git a/core/res/res/drawable/stat_notify_privacy_guard.xml b/core/res/res/drawable/stat_notify_privacy_guard.xml
new file mode 100644
index 0000000..de3aa77
--- /dev/null
+++ b/core/res/res/drawable/stat_notify_privacy_guard.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M21.539,8.978c-0.132,2.629-0.803,5.264-2.162,7.534 c-1.186,1.991-2.889,3.675-4.905,4.82c-0.459,0.259-0.932,0.494-1.427,0.676c-0.061,0.029-0.122-0.014-0.18-0.032 c-1.047-0.415-2.019-1.006-2.909-1.693c-1.807-1.397-3.239-3.263-4.151-5.356c-0.725-1.651-1.137-3.429-1.297-5.222 C4.33,7.77,4.442,5.816,4.73,3.896c0.601,0.053,1.207,0.031,1.807-0.017c1.309-0.115,2.596-0.41,3.849-0.799 c0.888-0.283,1.766-0.604,2.611-0.999c1.13,0.523,2.311,0.935,3.512,1.26c1.549,0.412,3.157,0.666,4.763,0.562 c0.116,0.846,0.216,1.695,0.261,2.548c0.002,0.241,0.042,0.481,0.037,0.723C21.574,7.776,21.594,8.379,21.539,8.978z"
+ android:fillColor="#FFFFFFFF" />
+</vector>
diff --git a/core/res/res/layout/permission_confirmation_dialog.xml b/core/res/res/layout/permission_confirmation_dialog.xml
new file mode 100644
index 0000000..7f31a9c
--- /dev/null
+++ b/core/res/res/layout/permission_confirmation_dialog.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright (c) 2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
+**
+** Copyright 2012 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:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:orientation="vertical">
+
+ <TextView android:id="@+id/permission_text"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dp"
+ android:paddingTop="16dip"
+ android:paddingBottom="16dip" />
+
+ <CheckBox android:id="@+id/permission_remember_choice_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/permission_remember_choice" />
+
+</LinearLayout>
diff --git a/core/res/res/values/cm_strings.xml b/core/res/res/values/cm_strings.xml
new file mode 100644
index 0000000..301131e
--- /dev/null
+++ b/core/res/res/values/cm_strings.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The LineageOS Project
+ Copyright (C) 2012-2014 The CyanogenMod Project
+ Copyright (c) 2013, The Linux Foundation. All rights reserved.
+
+ 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- ADB over network notification -->
+ <string name="adb_net_active_notification_title">ADB over network enabled</string>
+ <!-- ADB over USB and network notification -->
+ <string name="adb_both_active_notification_title">ADB over USB & network enabled</string>
+ <!-- ADB notification message-->
+ <string name="adb_active_generic_notification_message">Touch to disable debugging.</string>
+
+ <!-- App ops requests -->
+ <string name="app_ops_access_camera">access the camera</string>
+ <string name="app_ops_access_location">access your location</string>
+ <string name="app_ops_access_notifications">read your notifications</string>
+ <string name="app_ops_activate_vpn">activate a VPN</string>
+ <string name="app_ops_add_voicemail">add a voicemail</string>
+ <string name="app_ops_app_start_foreground">start instant app in foreground</string>
+ <string name="app_ops_assist_screenshot">take a screenshot</string>
+ <string name="app_ops_assist_structure">use assist structure</string>
+ <string name="app_ops_audio_accessibility_volume">audio accessibility volume</string>
+ <string name="app_ops_auto_start">start at power up</string>
+ <string name="app_ops_bind_accessibility_service">bind accessibility service</string>
+ <string name="app_ops_bluetooth_scan">bluetooth scan</string>
+ <string name="app_ops_change_wallpaper">change the wallpaper</string>
+ <string name="app_ops_delete_call_log">delete your call log</string>
+ <string name="app_ops_delete_contacts">delete your contacts</string>
+ <string name="app_ops_delete_mms">delete your MMS messages</string>
+ <string name="app_ops_delete_sms">delete your SMS messages</string>
+ <string name="app_ops_draw_on_top">draw windows on top</string>
+ <string name="app_ops_get_accounts">get device accounts</string>
+ <string name="app_ops_get_usage_stats">get app usage stats</string>
+ <string name="app_ops_accept_handover">handover of a call from another app</string>
+ <string name="app_ops_install_packages">install packages</string>
+ <string name="app_ops_keep_device_awake">keep your device awake</string>
+ <string name="app_ops_manage_ipsec_tunnels">manage ipsec tunnels</string>
+ <string name="app_ops_make_phone_call">make a phone call</string>
+ <string name="app_ops_mock_location">mock your location</string>
+ <string name="app_ops_modify_calendar">update your calendar</string>
+ <string name="app_ops_modify_call_log">update the call log</string>
+ <string name="app_ops_modify_clipboard">modify the clipboard</string>
+ <string name="app_ops_modify_contacts">update your contacts</string>
+ <string name="app_ops_modify_settings">update system settings</string>
+ <string name="app_ops_mute_unmute_microphone">mute/unmute the microphone</string>
+ <string name="app_ops_phone_calls">answer phone calls</string>
+ <string name="app_ops_picture_in_picture">use picture in picture</string>
+ <string name="app_ops_play_audio">play audio</string>
+ <string name="app_ops_post_notification">post a notification</string>
+ <string name="app_ops_project_media">project media</string>
+ <string name="app_ops_read_calendar">read your calendar</string>
+ <string name="app_ops_read_call_log">read the call log</string>
+ <string name="app_ops_read_cell_broadcasts">read cell broadcasts</string>
+ <string name="app_ops_read_clipboard">read the clipboard</string>
+ <string name="app_ops_read_contacts">read your contacts</string>
+ <string name="app_ops_read_external_storage">read external storage</string>
+ <string name="app_ops_read_mms">read your MMS messages</string>
+ <string name="app_ops_read_phone_numbers">read phone numbers</string>
+ <string name="app_ops_read_phone_state">access phone state</string>
+ <string name="app_ops_read_sms">read your SMS messages</string>
+ <string name="app_ops_receive_sms">receive an SMS message</string>
+ <string name="app_ops_receive_emergency_broadcast">receive an emergency broadcast message</string>
+ <string name="app_ops_record_audio">record audio</string>
+ <string name="app_ops_request_delete_packages">request delete packages</string>
+ <string name="app_ops_run_in_background">run in background</string>
+ <string name="app_ops_run_any_in_background">run any in background</string>
+ <string name="app_ops_scan_wifi">scan Wi-Fi networks</string>
+ <string name="app_ops_send_mms">send an MMS message</string>
+ <string name="app_ops_send_sms">send an SMS message</string>
+ <string name="app_ops_start_at_bootup">start at power up</string>
+ <string name="app_ops_start_foreground">start foreground</string>
+ <string name="app_ops_su">get root access</string>
+ <string name="app_ops_toast_window">display toast messages</string>
+ <string name="app_ops_toggle_bluetooth">toggle Bluetooth</string>
+ <string name="app_ops_toggle_mobile_data">toggle cellular data</string>
+ <string name="app_ops_toggle_nfc">toggle NFC</string>
+ <string name="app_ops_toggle_wifi">toggle Wi-Fi</string>
+ <string name="app_ops_turn_on_screen">turn the screen on</string>
+ <string name="app_ops_use_alarm_volume">control alarm volume</string>
+ <string name="app_ops_use_audio_focus">control the audio focus</string>
+ <string name="app_ops_use_bluetooth_volume">control the Bluetooth volume</string>
+ <string name="app_ops_use_body_sensors">use body sensors</string>
+ <string name="app_ops_use_fingerprint">use fingerprint</string>
+ <string name="app_ops_use_master_volume">control the master volume</string>
+ <string name="app_ops_use_media_buttons">use the media buttons</string>
+ <string name="app_ops_use_media_volume">control the media volume</string>
+ <string name="app_ops_use_notification_volume">control the notification volume</string>
+ <string name="app_ops_use_ring_volume">control the ringtone volume</string>
+ <string name="app_ops_use_vibrate">use haptic feedback</string>
+ <string name="app_ops_use_voice_volume">control the voice call volume</string>
+ <string name="app_ops_wifi_change">change Wi-Fi state</string>
+ <string name="app_ops_write_external_storage">write external storage</string>
+ <string name="app_ops_write_mms">write an MMS message</string>
+ <string name="app_ops_write_sms">write an SMS message</string>
+
+ <!-- Privacy Guard -->
+ <string name="permlab_changePrivacyGuardState">enable or disable Privacy Guard</string>
+ <string name="permdesc_changePrivacyGuardState">Allows the app to change whether another app runs with Privacy Guard. When an app is running with Privacy Guard, it will not have access to personal data such as contacts, call logs, or messages.</string>
+ <string name="privacy_guard_notification">Privacy Guard active</string>
+ <string name="privacy_guard_notification_detail"><xliff:g id="app">%1$s</xliff:g> will not be able to access personal data</string>
+ <string name="privacy_guard_dialog_title">Privacy Guard</string>
+ <string name="privacy_guard_dialog_summary"><xliff:g id="app">%1$s</xliff:g> would like to <xliff:g id="op">%2$s</xliff:g>.</string>
+
+ <!-- Text of the checkbox for the permission confirmation dialog to remember the user's choice. [CHAR LIMIT=40] -->
+ <string name="permission_remember_choice">Remember my choice</string>
+
+</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index da6b19d..af15239 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -56,6 +56,7 @@
<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_battery</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_su</xliff:g></item>
</string-array>
<string translatable="false" name="status_bar_rotate">rotate</string>
@@ -88,6 +89,7 @@
<string translatable="false" name="status_bar_vpn">vpn</string>
<string translatable="false" name="status_bar_ethernet">ethernet</string>
<string translatable="false" name="status_bar_airplane">airplane</string>
+ <string translatable="false" name="status_bar_su">su</string>
<!-- Flag indicating whether the surface flinger has limited
alpha compositing functionality in hardware. If set, the window
@@ -2442,6 +2444,39 @@
<item>com.android.inputmethod.latin</item>
</string-array>
+ <!-- The list of special carrier applications which should be disabled until used.
+ These apps are *not* carrier-privileged apps (e.g. because the carrier does not support the
+ feature itself) and are handled through matching UICC operator codes.
+ (This approach makes the matching rules static - manufacturer-controlled - and not
+ carrier-controlled.)
+ This function suppresses update notifications for these pre-installed apps.
+ In SubscriptionInfoUpdater, the listed applications are disabled until used when all of the
+ following conditions are met:
+ 1. Not currently carrier-matched according to the inserted UICC operator code
+ 2. Pre-installed
+ 3. In the default state (enabled but not explicitly)
+ And SubscriptionInfoUpdater undoes this and marks the app enabled when a UICC is inserted
+ that matches short-listed operator codes.
+ Each item here MUST have matching items (same index) in
+ #config_disabledUntilUsedSpecialCarrierAppUiccOperators, and
+ #config_disabledUntilUsedSpecialCarrierAppIntents.
+ Also, each item here MUST be a unique package name. -->
+ <string-array name="config_disabledUntilUsedSpecialCarrierApps" translatable="false" />
+
+ <!-- The list of UICC operator codes to enable a special carrier application for.
+ An operator code is in the form of "<MCC><MNC>" where MCC is three digits long and MNC
+ either two or three digits long.
+ An empty item is NOT permitted (it will be ignored).
+ If several operator codes are needed, they MUST be separated by commas.
+ Each package name listed in #config_disabledUntilUsedSpecialCarrierApps should have a
+ matching entry (same index) here. -->
+ <string-array name="config_disabledUntilUsedSpecialCarrierAppUiccOperators" translatable="false" />
+
+ <!-- The list of intent actions to broadcast when enabling a special carrier application.
+ Each package name listed in #config_disabledUntilUsedSpecialCarrierApps should have a
+ matching entry (same index) here, even if empty. -->
+ <string-array name="config_disabledUntilUsedSpecialCarrierAppIntents" translatable="false" />
+
<!-- The list of classes that should be added to the notification ranking pipeline.
See {@link com.android.server.notification.NotificationSignalExtractor}
If you add a new extractor to this list make sure to update
diff --git a/core/res/res/values/lineage_arrays.xml b/core/res/res/values/lineage_arrays.xml
new file mode 100644
index 0000000..58567d1
--- /dev/null
+++ b/core/res/res/values/lineage_arrays.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2012-2015 The CyanogenMod 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Do not translate. App ops request strings -->
+ <string-array name="app_ops_labels" translatable="false">
+ <!-- OP_COARSE_LOCATION -->
+ <item>@string/app_ops_access_location</item>
+ <!-- OP_FINE_LOCATION -->
+ <item>@string/app_ops_access_location</item>
+ <!-- OP_GPS -->
+ <item>@string/app_ops_access_location</item>
+ <!-- OP_VIBRATE -->
+ <item>@string/app_ops_use_vibrate</item>
+ <!-- OP_READ_CONTACTS -->
+ <item>@string/app_ops_read_contacts</item>
+ <!-- OP_WRITE_CONTACTS -->
+ <item>@string/app_ops_modify_contacts</item>
+ <!-- OP_READ_CALL_LOG -->
+ <item>@string/app_ops_read_call_log</item>
+ <!-- OP_WRITE_CALL_LOG -->
+ <item>@string/app_ops_modify_call_log</item>
+ <!-- OP_READ_CALENDAR -->
+ <item>@string/app_ops_read_calendar</item>
+ <!-- OP_WRITE_CALENDAR -->
+ <item>@string/app_ops_modify_calendar</item>
+ <!-- OP_WIFI_SCAN -->
+ <item>@string/app_ops_scan_wifi</item>
+ <!-- OP_POST_NOTIFICATION -->
+ <item>@string/app_ops_post_notification</item>
+ <!-- OP_NEIGHBORING_CELLS -->
+ <item>@string/app_ops_access_location</item>
+ <!-- OP_CALL_PHONE -->
+ <item>@string/app_ops_make_phone_call</item>
+ <!-- OP_READ_SMS -->
+ <item>@string/app_ops_read_sms</item>
+ <!-- OP_WRITE_SMS -->
+ <item>@string/app_ops_write_sms</item>
+ '<!-- OP_RECEIVE_SMS -->
+ <item>@string/app_ops_receive_sms</item>
+ <!-- OPSTR_RECEIVE_EMERGENCY_BROADCAST -->
+ <item>@string/app_ops_receive_emergency_broadcast</item>
+ <!-- OP_RECEIVE_MMS -->
+ <item>@string/app_ops_receive_sms</item>
+ <!-- OP_RECEIVE_WAP_PUSH -->
+ <item>@string/app_ops_receive_sms</item>
+ <!-- OP_SEND_SMS -->
+ <item>@string/app_ops_send_sms</item>
+ <!-- OP_READ_ICC_SMS -->
+ <item>@string/app_ops_read_sms</item>
+ <!-- OP_WRITE_ICC_SMS -->
+ <item>@string/app_ops_write_sms</item>
+ <!-- OP_WRITE_SETTINGS -->
+ <item>@string/app_ops_modify_settings</item>
+ <!-- OP_SYSTEM_ALERT_WINDOW -->
+ <item>@string/app_ops_draw_on_top</item>
+ <!-- OP_ACCESS_NOTIFICATIONS -->
+ <item>@string/app_ops_access_notifications</item>
+ <!-- OP_CAMERA -->
+ <item>@string/app_ops_access_camera</item>
+ <!-- OP_RECORD_AUDIO -->
+ <item>@string/app_ops_record_audio</item>
+ <!-- OP_PLAY_AUDIO -->
+ <item>@string/app_ops_play_audio</item>
+ <!-- OP_READ_CLIPBOARD -->
+ <item>@string/app_ops_read_clipboard</item>
+ <!-- OP_WRITE_CLIPBOARD -->
+ <item>@string/app_ops_modify_clipboard</item>
+ <!-- OP_TAKE_MEDIA_BUTTONS -->
+ <item>@string/app_ops_use_media_buttons</item>
+ <!-- OP_TAKE_AUDIO_FOCUS -->
+ <item>@string/app_ops_use_audio_focus</item>
+ <!-- OP_AUDIO_MASTER_VOLUME -->
+ <item>@string/app_ops_use_master_volume</item>
+ <!-- OP_AUDIO_VOICE_VOLUME -->
+ <item>@string/app_ops_use_voice_volume</item>
+ <!-- OP_AUDIO_RING_VOLUME -->
+ <item>@string/app_ops_use_ring_volume</item>
+ <!-- OP_AUDIO_MEDIA_VOLUME -->
+ <item>@string/app_ops_use_media_volume</item>
+ <!-- OP_AUDIO_ALARM_VOLUME -->
+ <item>@string/app_ops_use_alarm_volume</item>
+ <!-- OP_AUDIO_NOTIFICATION_VOLUME -->
+ <item>@string/app_ops_use_notification_volume</item>
+ <!-- OP_AUDIO_BLUETOOTH_VOLUME -->
+ <item>@string/app_ops_use_bluetooth_volume</item>
+ <!-- OP_WAKE_LOCK -->
+ <item>@string/app_ops_keep_device_awake</item>
+ <!-- OP_MONITOR_LOCATION -->
+ <item>@string/app_ops_access_location</item>
+ <!-- OP_MONITOR_HIGH_POWER_LOCATION -->
+ <item>@string/app_ops_access_location</item>
+ <!-- OP_GET_USAGE_STATS -->
+ <item>@string/app_ops_get_usage_stats</item>
+ <!-- OP_MUTE_MICROPHONE -->
+ <item>@string/app_ops_mute_unmute_microphone</item>
+ <!-- OP_TOAST_WINDOW -->
+ <item>@string/app_ops_toast_window</item>
+ <!-- OP_PROJECT_MEDIA -->
+ <item>@string/app_ops_project_media</item>
+ <!-- OP_ACTIVATE_VPN -->
+ <item>@string/app_ops_activate_vpn</item>
+ <!-- OP_WRITE_WALLPAPER -->
+ <item>@string/app_ops_change_wallpaper</item>
+ <!-- OP_ASSIST_STRUCTURE -->
+ <item>@string/app_ops_assist_structure</item>
+ <!-- OP_ASSIST_SCREENSHOT -->
+ <item>@string/app_ops_assist_screenshot</item>
+ <!-- OP_READ_PHONE_STATE -->
+ <item>@string/app_ops_read_phone_state</item>
+ <!-- OP_ADD_VOICEMAIL -->
+ <item>@string/app_ops_add_voicemail</item>
+ <!-- OP_USE_SIP -->
+ <item>@string/app_ops_make_phone_call</item>
+ <!-- OP_PROCESS_OUTGOING_CALLS -->
+ <item>@string/app_ops_make_phone_call</item>
+ <!-- OP_USE_FINGERPRINT -->
+ <item>@string/app_ops_use_fingerprint</item>
+ <!-- OP_BODY_SENSORS -->
+ <item>@string/app_ops_use_body_sensors</item>
+ <!-- OP_READ_CELL_BROADCASTS -->
+ <item>@string/app_ops_read_cell_broadcasts</item>
+ <!-- OP_MOCK_LOCATION -->
+ <item>@string/app_ops_mock_location</item>
+ <!-- OP_READ_EXTERNAL_STORAGE -->
+ <item>@string/app_ops_read_external_storage</item>
+ <!-- OP_WRITE_EXTERNAL_STORAGE -->
+ <item>@string/app_ops_write_external_storage</item>
+ <!-- OP_TURN_SCREEN_ON -->
+ <item>@string/app_ops_turn_on_screen</item>
+ <!-- OP_GET_ACCOUNTS -->
+ <item>@string/app_ops_get_accounts</item>
+ <!-- OP_RUN_IN_BACKGROUND -->
+ <item>@string/app_ops_run_in_background</item>
+ <!-- OP_AUDIO_ACCESSIBILITY_VOLUME -->
+ <item>@string/app_ops_audio_accessibility_volume</item>
+ <!-- OP_READ_PHONE_NUMBERS -->
+ <item>@string/app_ops_read_phone_numbers</item>
+ <!-- OP_REQUEST_INSTALL_PACKAGES -->
+ <item>@string/app_ops_install_packages</item>
+ <!-- OP_PICTURE_IN_PICTURE -->
+ <item>@string/app_ops_picture_in_picture</item>
+ <!-- OP_INSTANT_APP_START_FOREGROUND -->
+ <item>@string/app_ops_app_start_foreground</item>
+ <!-- OP_ANSWER_PHONE_CALLS -->
+ <item>@string/app_ops_phone_calls</item>
+ <!-- OP_RUN_ANY_IN_BACKGROUND -->
+ <item>@string/app_ops_run_any_in_background</item>
+ <!-- OP_CHANGE_WIFI_STATE -->
+ <item>@string/app_ops_wifi_change</item>
+ <!-- OP_REQUEST_DELETE_PACKAGES -->
+ <item>@string/app_ops_request_delete_packages</item>
+ <!-- OP_BIND_ACCESSIBILITY_SERVICE -->
+ <item>@string/app_ops_bind_accessibility_service</item>
+ <!-- OP_ACCEPT_HANDOVER -->
+ <item>@string/app_ops_accept_handover</item>
+ <!-- OP_MANAGE_IPSEC_TUNNELS -->
+ <item>@string/app_ops_manage_ipsec_tunnels</item>
+ <!-- OP_START_FOREGROUND -->
+ <item>@string/app_ops_start_foreground</item>
+ <!-- OP_BLUETOOTH_SCAN -->
+ <item>@string/app_ops_bluetooth_scan</item>
+ <!-- OP_BLUETOOTH_CHANGE -->
+ <item>@string/app_ops_toggle_bluetooth</item>
+ <!-- OP_BOOT_COMPLETED -->
+ <item>@string/app_ops_start_at_bootup</item>
+ <!-- OP_NFC_CHANGE -->
+ <item>@string/app_ops_toggle_nfc</item>
+ <!-- OP_DATA_CONNECT_CHANGE -->
+ <item>@string/app_ops_toggle_mobile_data</item>
+ <!-- OP_SU -->
+ <item>@string/app_ops_su</item>
+ </string-array>
+
+</resources>
diff --git a/core/res/res/values/lineage_symbols.xml b/core/res/res/values/lineage_symbols.xml
new file mode 100644
index 0000000..057e0f6
--- /dev/null
+++ b/core/res/res/values/lineage_symbols.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright (C) 2012-2015 The CyanogenMod Project
+ Copyright (C) 2017-2018 The LineageOS 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.
+-->
+<resources>
+ <!-- Privacy Guard -->
+ <java-symbol type="drawable" name="stat_notify_privacy_guard" />
+ <java-symbol type="string" name="privacy_guard_notification" />
+ <java-symbol type="string" name="privacy_guard_notification_detail" />
+ <java-symbol type="string" name="privacy_guard_dialog_title" />
+ <java-symbol type="string" name="privacy_guard_dialog_summary" />
+ <java-symbol type="id" name="permission_text" />
+ <java-symbol type="id" name="permission_remember_choice_checkbox" />
+ <java-symbol type="string" name="allow" />
+ <java-symbol type="string" name="deny" />
+ <java-symbol type="layout" name="permission_confirmation_dialog" />
+ <java-symbol type="array" name="app_ops_labels" />
+ <java-symbol type="string" name="status_bar_su" />
+</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 79b3740..140cfd9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1247,6 +1247,9 @@
<java-symbol type="array" name="supported_locales" />
<java-symbol type="array" name="config_cdma_dun_supported_types" />
<java-symbol type="array" name="config_disabledUntilUsedPreinstalledImes" />
+ <java-symbol type="array" name="config_disabledUntilUsedSpecialCarrierApps" />
+ <java-symbol type="array" name="config_disabledUntilUsedSpecialCarrierAppUiccOperators" />
+ <java-symbol type="array" name="config_disabledUntilUsedSpecialCarrierAppIntents" />
<java-symbol type="array" name="config_callBarringMMI" />
<java-symbol type="array" name="config_globalActionsList" />
<java-symbol type="array" name="config_telephonyEuiccDeviceCapabilities" />
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index 11432d6..3e21c3f 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -29,6 +29,13 @@
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
+#include <cutils/properties.h>
+
+// Direct access to private HAL data to workaround Adreno 330 driver bugs.
+// DO NOT actually call anything from this header; it's included only to access
+// private structs.
+#include "../../../../hardware/qcom/display/libgralloc/gralloc_priv.h"
+
namespace android {
namespace uirenderer {
@@ -62,9 +69,134 @@
return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap);
}
+namespace {
+
+CopyResult copyFromPrivateHandle(GraphicBuffer* graphicBuffer, Matrix4& texTransform,
+ const Rect& srcRect, SkBitmap* bitmap) {
+
+ static const bool workaround_enabled =
+ [] () -> bool {
+ const int value = property_get_bool("hwui.private_hal_readback", 0) == 1;
+ ALOGD("copyFromPrivateHandle: hwui.private_hal_readback=%i", value);
+ return bool(value);
+ }();
+
+ if (!workaround_enabled) {
+ return CopyResult::UnknownError;
+ }
+
+ static const Matrix4 defaultTransform = []() {
+ const float matrixData[] = {
+ 1.f, 0.f, 0.f, 0.f,
+ 0.f, -1.f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, 1.f, 0.f, 1.f
+ };
+ return Matrix4(matrixData);
+ }();
+
+ const int bitmapWidth = bitmap->width();
+ const int bitmapHeight = bitmap->height();
+
+ if (static_cast<int64_t>(graphicBuffer->getWidth()) != bitmapWidth
+ || static_cast<int64_t>(graphicBuffer->getHeight()) != bitmapHeight
+ || (!srcRect.isEmpty() &&
+ (srcRect.getWidth() != bitmapWidth || srcRect.getHeight() != bitmapHeight))
+ || texTransform != defaultTransform) {
+ // Image transformation isn't supported in any way here. Fall back to
+ // the default implementation.
+ // See CtsViewTestCases/android.view.cts.PixelCopyTest for the various
+ // ways in which scaling needs to be supported.
+ ALOGI("copyFromPrivateHandle: Image transformation is requested but not supported. "
+ "Falling back to the default implementation.");
+ return CopyResult::UnknownError;
+ }
+
+ if (bitmap->colorType() != kRGBA_8888_SkColorType) {
+ ALOGI("copyFromPrivateHandle: Only RGBA_8888 is supported.");
+ return CopyResult::SourceInvalid;
+ }
+ const size_t bytesPerPixel = bitmap->bytesPerPixel();
+
+ const native_handle_t* native_handle = graphicBuffer->handle;
+ const bool handleIsAsExpected = private_handle_t::validate(native_handle) == 0;
+ if (!handleIsAsExpected) {
+ ALOGE("copyFromPrivateHandle: GraphicBuffer doesn't seem to map to gralloc private handle.");
+ return CopyResult::SourceInvalid;
+ }
+ const private_handle_t* hnd = static_cast<const private_handle_t*>(native_handle);
+
+ // May be aligned and be larger than the actual image.
+ const int bufferWidth = hnd->width;
+ const int bufferHeight = hnd->height;
+ if (bitmapWidth > bufferWidth || bitmapHeight > bufferHeight) {
+ ALOGE("copyFromPrivateHandle: bitmap is larger than the buffer. This is not supposed to happen.");
+ return CopyResult::SourceInvalid;
+ }
+ const size_t bufferRowBytes = bufferWidth * bytesPerPixel;
+ // We access as many rows as the bitmap has, aligned to the width of the
+ // buffer.
+ const size_t minBufferSize = bufferRowBytes * bitmapHeight;
+ if (hnd->size < 0 || static_cast<size_t>(hnd->size) < minBufferSize) {
+ ALOGE("copyFromPrivateHandle: buffer is smaller than expected or invalid.");
+ return CopyResult::SourceInvalid;
+ }
+
+ const char* bufferData = reinterpret_cast<const char*>(hnd->base);
+
+ if (srcRect.isEmpty() && bitmapWidth == bufferWidth) {
+ // Take the quick path if possible
+ void* bitmapPixelAddr = bitmap->getPixels();
+ if (!bitmapPixelAddr) {
+ ALOGE("copyFromPrivateHandle: Bitmap pixel address is NULL");
+ return CopyResult::DestinationInvalid;
+ }
+ memcpy(bitmapPixelAddr, bufferData, static_cast<size_t>(hnd->size));
+ } else {
+ // The buffer has some extra space for alignment or we copy only a
+ // subset, or both. Copy requested pixels row-by-row.
+ int left = 0, top = 0;
+ if (!srcRect.isEmpty()) {
+ // Assuming that the floats in srcRect are actually integers.
+ left = static_cast<int>(srcRect.left);
+ top = static_cast<int>(srcRect.top);
+ }
+ // Check that the selected rect is within buffer range. Top/left corner
+ // is defined by srcRect, storage width and height by the bitmap, as
+ // done in equivalent GL implementation.
+ if (left + bitmapWidth > bufferWidth || top + bitmapHeight > bufferHeight) {
+ ALOGE("copyFromPrivateHandle: srcRect is larger than the buffer. This is not supposed to happen.");
+ return CopyResult::DestinationInvalid;
+ }
+ const size_t bitmapRowBytes = bitmapWidth * bytesPerPixel;
+ const char* bufferTop = bufferData + bufferRowBytes * top;
+ for (int y = 0; y < bitmapHeight; ++y) {
+ void* bitmapRowAddr = bitmap->getAddr(0, y);
+ if (!bitmapRowAddr) {
+ ALOGE("copyFromPrivateHandle: Bitmap address is NULL for row %i", y);
+ return CopyResult::DestinationInvalid;
+ }
+ const void* bufferRowAddr = bufferTop
+ + static_cast<size_t>(y) * bufferRowBytes
+ + static_cast<size_t>(left) * bytesPerPixel;
+ memcpy(bitmapRowAddr, bufferRowAddr, bitmapRowBytes);
+ }
+ }
+
+ bitmap->notifyPixelsChanged();
+
+ return CopyResult::Success;
+}
+
+} // anonymous namespace
+
CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
Matrix4& texTransform, const Rect& srcRect,
SkBitmap* bitmap) {
+ if (copyFromPrivateHandle(graphicBuffer, texTransform, srcRect, bitmap) == CopyResult::Success) {
+ return CopyResult::Success;
+ }
+
mRenderThread.eglManager().initialize();
// TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
// GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 263d249..c6ef090 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -223,6 +223,7 @@
break;
case PixelStorageType::Heap:
free(mPixelStorage.heap.address);
+ mallopt(M_PURGE, 0);
break;
case PixelStorageType::Hardware:
auto buffer = mPixelStorage.hardware.buffer;
@@ -230,7 +231,6 @@
mPixelStorage.hardware.buffer = nullptr;
break;
}
-
android::uirenderer::renderthread::RenderProxy::onBitmapDestroyed(getStableID());
}
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 4f16ddb..6e7511d 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -82,7 +82,14 @@
textureMatrix = textureMatrixInv;
}
- SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+ SkMatrix matrix;
+ if (dstRect) {
+ // Destination rectangle is set only when we are trying to read back the content
+ // of the layer. In this case we don't want to apply layer transform.
+ matrix = textureMatrix;
+ } else {
+ matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+ }
SkPaint paint;
paint.setAlpha(layer->getAlpha());
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 107890e..0760f16 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -26,6 +26,7 @@
#include "DeviceInfo.h"
#include "Matrix.h"
#include "Properties.h"
+#include "utils/MathUtils.h"
using namespace android::uirenderer::renderthread;
@@ -116,9 +117,9 @@
paint.setBlendMode(SkBlendMode::kSrc);
// Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage
// is codified by tests using golden images like DecodeAccuracyTest.
- if (skiaSrcRect.width() != bitmap->width() ||
- skiaSrcRect.height() != bitmap->height()) {
- // TODO: apply filter always, but check if tests will be fine
+ bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width())
+ && MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
+ if (!disableFilter) {
paint.setFilterQuality(kLow_SkFilterQuality);
}
scaledSurface->getCanvas()->concat(textureMatrix);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index d8f0886..0270796 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -207,7 +207,6 @@
mConnectivityManager = connectivityManager;
// check if verbose logging developer option has been turned on or off
- sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0);
mFilter = filter;
@@ -729,6 +728,7 @@
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
+ sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0);
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
updateWifiState(
diff --git a/packages/SystemUI/res/drawable/stat_sys_su.xml b/packages/SystemUI/res/drawable/stat_sys_su.xml
new file mode 100644
index 0000000..8e3e65f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_su.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="2.5dp"
+ android:insetRight="2.5dp">
+ <vector
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="19.3"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12.49,15.934h-2.373L9.24,21H6.966l0.89-5.066H5.051v-2.089h3.163l0.63-3.584H5.977V8.148h3.238 l0.902-5.141h2.261l-0.902,5.141h2.373l0.914-5.141h2.261l-0.902,5.141h2.719v2.113h-3.089l-0.63,3.584h2.78v2.089h-3.139L13.874,21 H11.6L12.49,15.934Z M10.488,13.845h2.36l0.63-3.584h-2.373L10.488,13.845z" />
+ </vector>
+</inset>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index e67bb60..3bfa12c 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -19,18 +19,16 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:theme="@style/qs_theme">
- <!-- right-aligned to be physically near volume button -->
<LinearLayout
android:id="@+id/volume_dialog"
android:minWidth="@dimen/volume_dialog_panel_width"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|right"
android:background="@android:color/transparent"
- android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_right"
+ android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_horizontal"
android:paddingTop="@dimen/volume_dialog_panel_transparent_padding"
android:paddingBottom="@dimen/volume_dialog_panel_transparent_padding"
- android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding"
+ android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding_horizontal"
android:orientation="vertical"
android:clipToPadding="false" >
@@ -40,7 +38,6 @@
android:layout_height="@dimen/volume_dialog_ringer_size"
android:layout_marginBottom="@dimen/volume_dialog_spacer"
android:translationZ="@dimen/volume_dialog_elevation"
- android:layout_gravity="right"
android:clipToPadding="false"
android:background="@drawable/rounded_bg_full">
<com.android.keyguard.AlphaOptimizedImageButton
@@ -56,7 +53,6 @@
<include layout="@layout/volume_dnd_icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginRight="@dimen/volume_dialog_stream_padding"
android:layout_marginTop="6dp"/>
</FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f00957a..37187e1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -292,7 +292,7 @@
<!-- The width of the panel that holds the quick settings. -->
<dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
- <dimen name="volume_dialog_panel_transparent_padding_right">8dp</dimen>
+ <dimen name="volume_dialog_panel_transparent_padding_horizontal">8dp</dimen>
<dimen name="volume_dialog_panel_transparent_padding">20dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 9c9f021..c4ac1aa 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -89,6 +89,8 @@
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SuController;
+import com.android.systemui.statusbar.policy.SuControllerImpl;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -328,6 +330,8 @@
mProviders.put(IStatusBarService.class, () -> IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE)));
+ mProviders.put(SuController.class,() -> new SuControllerImpl(mContext));
+
// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
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 74af618..56c0528 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -85,6 +85,7 @@
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
+import com.android.systemui.statusbar.policy.SuController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.NotificationChannels;
@@ -118,6 +119,7 @@
private final String mSlotHeadset;
private final String mSlotDataSaver;
private final String mSlotLocation;
+ private final String mSlotSu;
private final Context mContext;
private final Handler mHandler = new Handler();
@@ -136,6 +138,7 @@
private final LocationController mLocationController;
private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ private final SuController mSuController;
// Assume it's all good unless we hear otherwise. We don't always seem
// to get broadcasts that it *is* there.
@@ -167,6 +170,7 @@
mProvisionedController = Dependency.get(DeviceProvisionedController.class);
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mLocationController = Dependency.get(LocationController.class);
+ mSuController = Dependency.get(SuController.class);
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
@@ -181,6 +185,7 @@
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);
+ mSlotSu = context.getString(com.android.internal.R.string.status_bar_su);
// listen for broadcasts
IntentFilter filter = new IntentFilter();
@@ -229,6 +234,10 @@
mContext.getString(R.string.accessibility_status_bar_hotspot));
mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());
+ // su
+ mIconController.setIcon(mSlotSu, R.drawable.stat_sys_su, null);
+ mIconController.setIconVisibility(mSlotSu, mSuController.getSessionCount() > 0);
+
// managed profile
mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
mContext.getString(R.string.accessibility_managed_profile));
@@ -245,6 +254,7 @@
mZenController.addCallback(this);
mCast.addCallback(mCastCallback);
mHotspot.addCallback(mHotspotCallback);
+ mSuController.addCallback(mSuCallback);
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
mKeyguardMonitor.addCallback(this);
@@ -273,6 +283,7 @@
mZenController.removeCallback(this);
mCast.removeCallback(mCastCallback);
mHotspot.removeCallback(mHotspotCallback);
+ mSuController.removeCallback(mSuCallback);
mNextAlarmController.removeCallback(mNextAlarmCallback);
mDataSaver.removeCallback(this);
mKeyguardMonitor.removeCallback(this);
@@ -700,6 +711,14 @@
}
};
+
+ private final SuController.Callback mSuCallback = new SuController.Callback() {
+ @Override
+ public void onSuSessionsChanged(int sessionCount) {
+ mIconController.setIconVisibility(mSlotSu, sessionCount > 0);
+ }
+ };
+
private final CastController.Callback mCastCallback = new CastController.Callback() {
@Override
public void onCastDevicesChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index d6d0673..0a1d451 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -114,11 +114,12 @@
private void updateWifiStateListeners(boolean shouldListen) {
mWifiStateReceiver.setListening(shouldListen);
if (shouldListen) {
- mWifiManager.registerSoftApCallback(
+ if(mWifiManager != null)
+ mWifiManager.registerSoftApCallback(
this,
Dependency.get(Dependency.MAIN_HANDLER));
- } else {
- mWifiManager.unregisterSoftApCallback(this);
+ } else {
+ mWifiManager.unregisterSoftApCallback(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 25261c0..16f8c03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -572,7 +572,7 @@
public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) {
return lhs.getSimSlotIndex() == rhs.getSimSlotIndex()
? lhs.getSubscriptionId() - rhs.getSubscriptionId()
- : lhs.getSimSlotIndex() - rhs.getSimSlotIndex();
+ : rhs.getSimSlotIndex() - lhs.getSimSlotIndex();
}
});
mCurrentSubscriptions = subscriptions;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuController.java
new file mode 100644
index 0000000..d3575c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuController.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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.systemui.statusbar.policy;
+
+public interface SuController {
+ void addCallback(Callback callback);
+ void removeCallback(Callback callback);
+ int getSessionCount();
+
+ public interface Callback {
+ void onSuSessionsChanged(int sessionCount);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuControllerImpl.java
new file mode 100644
index 0000000..d2661e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuControllerImpl.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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.systemui.statusbar.policy;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Handler;
+import android.util.Log;
+
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A controller to manage changes to superuser-related states and update the views accordingly.
+ */
+public class SuControllerImpl implements SuController, AppOpsManager.OnOpActiveChangedListener {
+ private static final String TAG = "SuControllerImpl";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final int[] SU_OPS = new int[] { AppOpsManager.OP_SU };
+
+ private ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+
+ private AppOpsManager mAppOpsManager;
+ private Set<String> mActivePackages = new HashSet<>();
+ private Handler mHandler = new Handler();
+
+ public SuControllerImpl(Context context) {
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ }
+
+ @Override
+ public void addCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ if (mCallbacks.isEmpty()) {
+ mAppOpsManager.startWatchingActive(SU_OPS, this);
+
+ synchronized (mActivePackages) {
+ mActivePackages.clear();
+ initActivePackagesLocked();
+ }
+ }
+ mCallbacks.add(callback);
+ callback.onSuSessionsChanged(mActivePackages.size());
+ }
+ }
+
+ @Override
+ public void removeCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ if (mCallbacks.isEmpty()) {
+ mAppOpsManager.stopWatchingActive(this);
+ }
+ }
+ }
+
+ @Override
+ public int getSessionCount() {
+ synchronized (mActivePackages) {
+ return mActivePackages.size();
+ }
+ }
+
+ private void initActivePackagesLocked() {
+
+ List<AppOpsManager.PackageOps> packages = mAppOpsManager.getPackagesForOps(SU_OPS);
+ if (packages != null) {
+ for (AppOpsManager.PackageOps ops : packages) {
+ if (mAppOpsManager.isOperationActive(AppOpsManager.OP_SU,
+ ops.getUid(), ops.getPackageName())) {
+ mActivePackages.add(ops.getPackageName());
+ }
+ }
+ }
+ }
+
+ private void fireCallbacks(int sessionCount) {
+ synchronized (mCallbacks) {
+ for (Callback callback : mCallbacks) {
+ callback.onSuSessionsChanged(sessionCount);
+ }
+ }
+ }
+
+ @Override
+ public void onOpActiveChanged(int op, int uid, String packageName, boolean active) {
+ if (DEBUG) Log.d(TAG, "SU active changed for " + packageName + " to " + active);
+ int oldCount, newCount;
+ synchronized (mActivePackages) {
+ oldCount = mActivePackages.size();
+ if (active) {
+ mActivePackages.add(packageName);
+ } else {
+ mActivePackages.remove(packageName);
+ }
+ newCount = mActivePackages.size();
+ }
+ if (oldCount != newCount) {
+ mHandler.post(() -> fireCallbacks(newCount));
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index e5e576d..b9bccef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -59,9 +59,12 @@
mHasMobileData = hasMobileData;
Handler handler = new WifiHandler(Looper.getMainLooper());
mWifiChannel = new AsyncChannel();
- Messenger wifiMessenger = wifiManager.getWifiServiceMessenger();
- if (wifiMessenger != null) {
- mWifiChannel.connect(context, handler, wifiMessenger);
+ if(wifiManager != null){
+ Messenger wifiMessenger = wifiManager.getWifiServiceMessenger();
+
+ if (wifiMessenger != null) {
+ mWifiChannel.connect(context, handler, wifiMessenger);
+ }
}
// WiFi only has one state.
mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index ed243ef..b5ebdb7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -149,6 +149,8 @@
private State mState;
private SafetyWarningDialog mSafetyWarning;
private boolean mHovering = false;
+ // Volume panel placement that is currently in use.
+ private boolean mVolumePanelOnLeft = true;
public VolumeDialogImpl(Context context) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
@@ -196,15 +198,19 @@
final WindowManager.LayoutParams lp = mWindow.getAttributes();
lp.format = PixelFormat.TRANSLUCENT;
lp.setTitle(VolumeDialogImpl.class.getSimpleName());
- lp.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
lp.windowAnimations = -1;
+ if (!mVolumePanelOnLeft) {
+ lp.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
+ } else {
+ lp.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
+ }
mWindow.setAttributes(lp);
mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mDialog.setCanceledOnTouchOutside(true);
mDialog.setContentView(R.layout.volume_dialog);
mDialog.setOnShowListener(dialog -> {
- if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2);
+ if (!isLandscape()) mDialogView.setTranslationX(getAnimatorX());
mDialogView.setAlpha(0);
mDialogView.animate()
.alpha(1)
@@ -245,11 +251,11 @@
addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
R.drawable.ic_volume_accessibility, true, false);
}
- addRow(AudioManager.STREAM_MUSIC,
- R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
+ addRow(AudioManager.STREAM_RING,
+ R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, true);
if (!AudioSystem.isSingleVolume(mContext)) {
- addRow(AudioManager.STREAM_RING,
- R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false);
+ addRow(AudioManager.STREAM_MUSIC,
+ R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, false);
addRow(STREAM_ALARM,
R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, true, false);
addRow(AudioManager.STREAM_VOICE_CALL,
@@ -268,6 +274,11 @@
initSettingsH();
}
+ private float getAnimatorX() {
+ float x = mDialogView.getWidth() / 2;
+ return mVolumePanelOnLeft ? -x : x;
+ }
+
protected ViewGroup getDialogView() {
return mDialogView;
}
@@ -310,7 +321,11 @@
if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream);
VolumeRow row = new VolumeRow();
initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
- mDialogRowsView.addView(row.view);
+ if (!mVolumePanelOnLeft) {
+ mDialogRowsView.addView(row.view, 0);
+ } else {
+ mDialogRowsView.addView(row.view);
+ }
mRows.add(row);
}
@@ -320,7 +335,11 @@
final VolumeRow row = mRows.get(i);
initRow(row, row.stream, row.iconRes, row.iconMuteRes, row.important,
row.defaultStream);
- mDialogRowsView.addView(row.view);
+ if (!mVolumePanelOnLeft) {
+ mDialogRowsView.addView(row.view, 0);
+ } else {
+ mDialogRowsView.addView(row.view);
+ }
updateVolumeRowH(row);
}
}
@@ -332,7 +351,7 @@
}
}
for (VolumeRow row : mRows) {
- if (row.stream == STREAM_MUSIC) {
+ if (row.stream == STREAM_RING) {
return row;
}
}
@@ -575,7 +594,7 @@
if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
mDialog.dismiss();
}, 50));
- if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2);
+ if (!isLandscape()) animator.translationX(getAnimatorX());
animator.start();
Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
@@ -606,7 +625,7 @@
}
if (row.defaultStream) {
- return activeRow.stream == STREAM_RING
+ return activeRow.stream == STREAM_MUSIC
|| activeRow.stream == STREAM_ALARM
|| activeRow.stream == STREAM_VOICE_CALL
|| activeRow.stream == STREAM_ACCESSIBILITY
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 4328d94..1a8c289 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -6119,5 +6119,13 @@
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
+
+ // ------- Begin Fairphone Constants -----
+
+ // OPEN: Settings > Maintenance
+ // CATEGORY: SETTINGS
+ FP_MAINTENANCE = 1500;
+
+ // ---- End Fairphone Constants, all Fairphone constants go above this line ----
}
}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 0950042..2efb0bd 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -40,6 +43,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -73,6 +77,8 @@
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.PermissionDialogReqQueue.PermissionDialogReq;
import libcore.util.EmptyArray;
@@ -87,6 +93,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.Math;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -181,10 +188,19 @@
OP_RECORD_AUDIO,
OP_CAMERA,
};
+ // Rate limiting thresholds for ask operations
+ public static final int RATE_LIMIT_OP_COUNT = 3;
+ public static final int RATE_LIMIT_OPS_TOTAL_PKG_COUNT = 4;
+ public static final int RATE_LIMIT_OP_DELAY_CEILING = 10;
Context mContext;
final AtomicFile mFile;
final Handler mHandler;
+ final Looper mLooper;
+
+ private boolean mIsInteractive = true;
+
+ private final ActivityManagerService mActivityManagerService;
private final AppOpsManagerInternalImpl mAppOpsManagerInternal
= new AppOpsManagerInternalImpl();
@@ -327,6 +343,9 @@
public SparseBooleanArray foregroundOps;
public boolean hasForegroundWatchers;
+ public int pendingAskOp;
+ public boolean receivedPendingAskResponse;
+
public UidState(int uid) {
this.uid = uid;
}
@@ -399,11 +418,13 @@
final String packageName;
final UidState uidState;
final boolean isPrivileged;
+ long startRealtime;
Ops(String _packageName, UidState _uidState, boolean _isPrivileged) {
packageName = _packageName;
uidState = _uidState;
isPrivileged = _isPrivileged;
+ startRealtime = 0;
}
}
@@ -420,6 +441,13 @@
long rejectTime[] = new long[_NUM_UID_STATE];
int startNesting;
long startRealtime;
+ int noteOpCount;
+ int startOpCount;
+ PermissionDialogReqQueue dialogReqQueue;
+ final ArrayList<IBinder> clientTokens;
+ public int allowedCount;
+ public int ignoredCount;
+ int delayedCount;
Op(UidState _uidState, String _packageName, int _op) {
uidState = _uidState;
@@ -427,6 +455,18 @@
packageName = _packageName;
op = _op;
mode = AppOpsManager.opToDefaultMode(op);
+ dialogReqQueue = new PermissionDialogReqQueue();
+ clientTokens = new ArrayList<IBinder>();
+ }
+
+ Op(UidState _uidState, String _packageName, int _op, int _mode) {
+ uidState = _uidState;
+ uid = _uidState.uid;
+ packageName = _packageName;
+ op = _op;
+ mode = _mode;
+ dialogReqQueue = new PermissionDialogReqQueue();
+ clientTokens = new ArrayList<IBinder>();
}
boolean hasAnyTime() {
@@ -578,18 +618,24 @@
public void binderDied() {
synchronized (AppOpsService.this) {
for (int i=mStartedOps.size()-1; i>=0; i--) {
- finishOperationLocked(mStartedOps.get(i), /*finishNested*/ true);
+ final Op op = mStartedOps.get(i);
+ finishOperationLocked(op, /*finishNested*/ true);
+ if (op.startNesting <= 0) {
+ scheduleOpActiveChangedIfNeededLocked(op.op, op.uid, op.packageName, false);
+ }
}
mClients.remove(mAppToken);
}
}
}
- public AppOpsService(File storagePath, Handler handler) {
+ public AppOpsService(File storagePath, Handler handler, ActivityManagerService service) {
LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
mFile = new AtomicFile(storagePath, "appops");
mHandler = handler;
mConstants = new Constants(mHandler);
+ mLooper = Looper.myLooper();
+ mActivityManagerService = service;
readState();
}
@@ -602,6 +648,11 @@
public void systemReady() {
mConstants.startMonitoring(mContext.getContentResolver());
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ mContext.registerReceiver(mIntentReceiver, filter);
+
synchronized (this) {
boolean changed = false;
for (int i = mUidStates.size() - 1; i >= 0; i--) {
@@ -702,15 +753,48 @@
if (Process.isIsolated(uid)) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
- if (noteOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE, uid,
- packageName) != AppOpsManager.MODE_ALLOWED) {
+ synchronized (AppOpsService.this) {
+ UidState uidState = getUidStateLocked(uid, false);
+ if (uidState != null && uidState.receivedPendingAskResponse) {
+ int op = uidState.pendingAskOp;
+ boolean write = op == AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
+
+ if (DEBUG) Slog.d(TAG, "getMountMode: allowing "
+ + (write ? "write" : "read") + " for uid "
+ + uid + " package " + packageName
+ + " due to previous response");
+
+ uidState.pendingAskOp = 0;
+ uidState.receivedPendingAskResponse = false;
+ return write
+ ? Zygote.MOUNT_EXTERNAL_WRITE : Zygote.MOUNT_EXTERNAL_READ;
+ }
+ }
+ int readResult = check(AppOpsManager.OP_READ_EXTERNAL_STORAGE,
+ uid, packageName);
+ if (readResult == AppOpsManager.MODE_IGNORED) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
- if (noteOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, uid,
- packageName) != AppOpsManager.MODE_ALLOWED) {
+ int writeResult = check(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE,
+ uid, packageName);
+ if (DEBUG) Slog.d(TAG, "getMountMode: read result " + readResult
+ + " write result " + writeResult + " for uid " + uid
+ + " package " + packageName);
+
+ if (writeResult == AppOpsManager.MODE_ALLOWED) {
+ return Zygote.MOUNT_EXTERNAL_WRITE;
+ } else if (writeResult == AppOpsManager.MODE_ASK) {
+ scheduleAskOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE,
+ uid, packageName);
+ return readResult == AppOpsManager.MODE_ALLOWED
+ ? Zygote.MOUNT_EXTERNAL_READ : Zygote.MOUNT_EXTERNAL_NONE;
+ } else if (readResult == AppOpsManager.MODE_ALLOWED) {
return Zygote.MOUNT_EXTERNAL_READ;
+ } else if (readResult == AppOpsManager.MODE_ASK) {
+ scheduleAskOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE,
+ uid, packageName);
}
- return Zygote.MOUNT_EXTERNAL_WRITE;
+ return Zygote.MOUNT_EXTERNAL_NONE;
}
@Override
@@ -719,9 +803,85 @@
return mountMode == Zygote.MOUNT_EXTERNAL_READ
|| mountMode == Zygote.MOUNT_EXTERNAL_WRITE;
}
+
+ private int check(int code, int uid, String packageName) {
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ return noteOperationUnchecked(code, uid,
+ resolvedPackageName, 0, null, true);
+ }
+
+ private void scheduleAskOperation(int code, int uid, String packageName) {
+ synchronized (AppOpsService.this) {
+ UidState uidState = getUidStateLocked(uid, true);
+ uidState.pendingAskOp = code;
+ uidState.receivedPendingAskResponse = false;
+ // Schedule noteOperation which will trigger the dialog
+ // NOTE: needs to happen in background thread, as otherwise main thread
+ // will block due to scheduling the dialog and waiting for it in
+ // the same thread
+ AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
+ @Override protected Void doInBackground(Void... params) {
+ noteOperation(code, uid, packageName);
+ return null;
+ }
+ };
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
+ }
+ }
});
}
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_SCREEN_ON)) {
+ synchronized (AppOpsService.this) {
+ mIsInteractive = true;
+ }
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ synchronized (AppOpsService.this) {
+ for (int i = mUidStates.size() - 1; i >= 0; i--) {
+ UidState uidState = mUidStates.valueAt(i);
+
+ ArrayMap<String, Ops> packages = uidState.pkgOps;
+ if (packages == null) {
+ continue;
+ }
+
+ Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, Ops> ent = it.next();
+ Ops pkgOps = ent.getValue();
+ for (int j = pkgOps.size() - 1; j >= 0; j--) {
+ Op curOp = pkgOps.valueAt(j);
+ if (DEBUG) Slog.d(TAG, "Ignoring " + curOp.packageName + " request "
+ + curOp.op);
+ curOp.dialogReqQueue.ignore();
+ }
+ }
+ }
+ mIsInteractive = false;
+ }
+ }
+ }
+ };
+
+ public void handlePackageResumed(int uid, String packageName) {
+ synchronized (this) {
+ Ops ops = getOpsRawLocked(uid, packageName, true, false);
+ if (ops == null) {
+ return;
+ }
+ ops.startRealtime = SystemClock.elapsedRealtime();
+ }
+ }
+
public void packageRemoved(int uid, String packageName) {
synchronized (this) {
UidState uidState = mUidStates.get(uid);
@@ -851,13 +1011,14 @@
resOps = new ArrayList<>();
for (int j=0; j<pkgOps.size(); j++) {
Op curOp = pkgOps.valueAt(j);
+ curOp.startRealtime = pkgOps.startRealtime;
final boolean running = curOp.duration == -1;
long duration = running
? (elapsedNow - curOp.startRealtime)
: curOp.duration;
resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
curOp.rejectTime, (int) duration, running, curOp.proxyUid,
- curOp.proxyPackageName));
+ curOp.proxyPackageName, curOp.allowedCount, curOp.ignoredCount));
}
} else {
for (int j=0; j<ops.length; j++) {
@@ -866,13 +1027,14 @@
if (resOps == null) {
resOps = new ArrayList<>();
}
+ curOp.startRealtime = pkgOps.startRealtime;
final boolean running = curOp.duration == -1;
final long duration = running
? (elapsedNow - curOp.startRealtime)
: curOp.duration;
resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
curOp.rejectTime, (int) duration, running, curOp.proxyUid,
- curOp.proxyPackageName));
+ curOp.proxyPackageName, curOp.allowedCount, curOp.ignoredCount));
}
}
}
@@ -885,7 +1047,7 @@
resOps = new ArrayList<>();
for (int j=0; j<uidOps.size(); j++) {
resOps.add(new AppOpsManager.OpEntry(uidOps.keyAt(j), uidOps.valueAt(j),
- 0, 0, 0, -1, null));
+ 0, 0, 0, -1, null, 0, 0));
}
} else {
for (int j=0; j<ops.length; j++) {
@@ -895,7 +1057,7 @@
resOps = new ArrayList<>();
}
resOps.add(new AppOpsManager.OpEntry(uidOps.keyAt(index), uidOps.valueAt(index),
- 0, 0, 0, -1, null));
+ 0, 0, 0, -1, null, 0, 0));
}
}
}
@@ -1029,7 +1191,8 @@
code = AppOpsManager.opToSwitch(code);
synchronized (this) {
- final int defaultMode = AppOpsManager.opToDefaultMode(code);
+ final int defaultMode = AppOpsManager.opToDefaultMode(code,
+ AppOpsManager.isStrictOp(code));
UidState uidState = getUidStateLocked(uid, false);
if (uidState == null) {
@@ -1589,7 +1752,7 @@
return AppOpsManager.MODE_IGNORED;
}
final int proxyMode = noteOperationUnchecked(code, proxyUid,
- resolveProxyPackageName, -1, null);
+ resolveProxyPackageName, -1, null, false);
if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
return proxyMode;
}
@@ -1598,7 +1761,7 @@
return AppOpsManager.MODE_IGNORED;
}
return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
- proxyMode, resolveProxyPackageName);
+ proxyMode, resolveProxyPackageName, false);
}
@Override
@@ -1609,11 +1772,12 @@
if (resolvedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
- return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null);
+ return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null, false);
}
private int noteOperationUnchecked(int code, int uid, String packageName,
- int proxyUid, String proxyPackageName) {
+ int proxyUid, String proxyPackageName, boolean avoidAskMode) {
+ PermissionDialogReq req = null;
synchronized (this) {
final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
false /* uidMismatchExpected */);
@@ -1624,6 +1788,7 @@
}
final Op op = getOpLocked(ops, code, true);
if (isOpRestrictedLocked(uid, code, packageName)) {
+ op.ignoredCount++;
return AppOpsManager.MODE_IGNORED;
}
final UidState uidState = ops.uidState;
@@ -1648,22 +1813,93 @@
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
final int mode = switchOp.getMode();
- if (mode != AppOpsManager.MODE_ALLOWED) {
+ if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_ASK) {
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
op.rejectTime[uidState.state] = System.currentTimeMillis();
+ op.ignoredCount++;
return mode;
+ } else if (mode == AppOpsManager.MODE_ASK) {
+ if (Looper.myLooper() == mLooper || Thread.holdsLock(mActivityManagerService)) {
+ Slog.e(TAG, "noteOperation: this method will deadlock if called" +
+ " from the main thread. (Code: " + code + " uid: " + uid +
+ " package: " + packageName + ")");
+ return mode;
+ } else if (avoidAskMode) {
+ return mode;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Package " + op.packageName + " has " + op.noteOpCount
+ + " requests and " + op.startOpCount + " start requests with "
+ + op.ignoredCount + " ignored at " + Arrays.toString(op.time) +
+ " with a duration of " + op.duration +
+ " while being delayed " + op.delayedCount + " times");
+ Slog.d(TAG, "Total pkops for " + ops.packageName + " "
+ + ops.uidState.pkgOps.size());
+ }
+
+ // We don't need ask everytime, just after app resume.
+ // Check once and remember the choice for 5 minutes.
+ final long lastcheck = Math.max(op.time[uidState.state],
+ op.rejectTime[uidState.state]);
+ if (lastcheck > ops.startRealtime &&
+ lastcheck < (ops.startRealtime + 5 * 60 * 1000L)) {
+ return op.time[uidState.state] >= op.rejectTime[uidState.state]
+ ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
+ }
+
+ // First drop all request events if the device is not interactive, next check
+ // what the global pkg ops count for the package, then check op scoped count.
+ // High frequency request ops will be delayed until their delay count ceiling
+ // is met. This is to mitigate the overloading the main activity manager
+ // service handler and having watchdog kill our service.
+ // Google Play services likes to share its uid with numerous packages to avoid
+ // having to grant permissions from the users perspective and thus is the worst
+ // example of overloading this queue -- so, to not encourage bad behavior,
+ // we move them to the back of the line.
+ //
+ // NOTE: these values are magic, and may need tuning. Ideally we'd want a
+ // ringbuffer or token bucket here to do proper rate limiting.
+ if (mIsInteractive &&
+ (ops.uidState.pkgOps.size() < RATE_LIMIT_OPS_TOTAL_PKG_COUNT
+ && op.noteOpCount < RATE_LIMIT_OP_COUNT
+ || op.delayedCount > RATE_LIMIT_OP_DELAY_CEILING)) {
+
+ // Reset delayed count, most ops will never need this
+ if (op.delayedCount > 0) {
+ if (DEBUG) Slog.d(TAG, "Resetting delayed count for " + op.packageName);
+ op.delayedCount = 0;
+ }
+
+ op.noteOpCount++;
+ req = askOperationLocked(code, uid, packageName, switchOp);
+ } else {
+ if (mIsInteractive) {
+ op.delayedCount++;
+ }
+ op.ignoredCount++;
+ return AppOpsManager.MODE_IGNORED;
+ }
}
}
- if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
- + " package " + packageName);
- op.time[uidState.state] = System.currentTimeMillis();
- op.rejectTime[uidState.state] = 0;
- op.proxyUid = proxyUid;
- op.proxyPackageName = proxyPackageName;
- return AppOpsManager.MODE_ALLOWED;
+ if (req == null) {
+ if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
+ + " package " + packageName);
+ op.time[uidState.state] = System.currentTimeMillis();
+ op.rejectTime[uidState.state] = 0;
+ op.proxyUid = proxyUid;
+ op.proxyPackageName = proxyPackageName;
+ op.allowedCount++;
+ return AppOpsManager.MODE_ALLOWED;
+ }
}
+
+ if (DEBUG) Slog.d(TAG, "noteOperation: waiting for result code " + code + " uid " + uid
+ + " package " + packageName);
+
+ return req.get();
}
@Override
@@ -1720,6 +1956,7 @@
@Override
public int startOperation(IBinder token, int code, int uid, String packageName,
boolean startIfModeDefault) {
+ final PermissionDialogReq req;
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -1737,51 +1974,63 @@
}
final Op op = getOpLocked(ops, code, true);
if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
+ op.ignoredCount++;
return AppOpsManager.MODE_IGNORED;
}
final int switchCode = AppOpsManager.opToSwitch(code);
final UidState uidState = ops.uidState;
- // If there is a non-default per UID policy (we set UID op mode only if
- // non-default) it takes over, otherwise use the per package policy.
- if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
- final int uidMode = uidState.evalMode(uidState.opModes.get(switchCode));
- if (uidMode != AppOpsManager.MODE_ALLOWED
- && (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) {
+ if (uidState.opModes != null) {
+ final int uidMode = uidState.opModes.get(switchCode);
+ if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
op.rejectTime[uidState.state] = System.currentTimeMillis();
return uidMode;
}
+ }
+ final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
+ final int mode = switchOp.getMode();
+ if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_ASK) {
+ if (DEBUG) Slog.d(TAG, "startOperation: reject #" + op.mode + " for code "
+ + switchCode + " (" + code + ") uid " + uid + " package "
+ + resolvedPackageName);
+ op.rejectTime[uidState.state] = System.currentTimeMillis();
+ op.ignoredCount++;
+ return mode;
+ } else if (mode == AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
+ + " package " + resolvedPackageName);
+ if (op.startNesting == 0) {
+ op.startRealtime = SystemClock.elapsedRealtime();
+ op.time[uidState.state] = System.currentTimeMillis();
+ op.rejectTime[uidState.state] = 0;
+ op.duration = -1;
+ op.allowedCount++;
+ scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
+ }
+ op.startNesting++;
+ uidState.startNesting++;
+ if (client.mStartedOps != null) {
+ client.mStartedOps.add(op);
+ }
+ return AppOpsManager.MODE_ALLOWED;
} else {
- final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
- final int mode = switchOp.getMode();
- if (mode != AppOpsManager.MODE_ALLOWED
- && (!startIfModeDefault || mode != AppOpsManager.MODE_DEFAULT)) {
- if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
- + switchCode + " (" + code + ") uid " + uid + " package "
- + resolvedPackageName);
- op.rejectTime[uidState.state] = System.currentTimeMillis();
+ if (Looper.myLooper() == mLooper || Thread.holdsLock(mActivityManagerService)) {
+ Slog.e(TAG, "startOperation: this method will deadlock if called" +
+ " from the main thread. (Code: " + code + " uid: " + uid +
+ " package: " + resolvedPackageName + ")");
return mode;
}
- }
- if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
- + " package " + resolvedPackageName);
- if (op.startNesting == 0) {
- op.startRealtime = SystemClock.elapsedRealtime();
- op.time[uidState.state] = System.currentTimeMillis();
- op.rejectTime[uidState.state] = 0;
- op.duration = -1;
- scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
- }
- op.startNesting++;
- uidState.startNesting++;
- if (client.mStartedOps != null) {
- client.mStartedOps.add(op);
+ if (DEBUG) Slog.d(TAG, "startOperation: asking for code " + code
+ + " uid " + uid + " package " + resolvedPackageName);
+ op.startOpCount++;
+ IBinder clientToken = client.mAppToken;
+ op.clientTokens.add(clientToken);
+ req = askOperationLocked(code, uid, resolvedPackageName, switchOp);
}
}
-
- return AppOpsManager.MODE_ALLOWED;
+ return req.get();
}
@Override
@@ -1904,6 +2153,10 @@
}
private void verifyIncomingUid(int uid) {
+ if (Binder.getCallingUid() == 0) {
+ // Allow root to delegate uid operations.
+ return;
+ }
if (uid == Binder.getCallingUid()) {
return;
}
@@ -2333,15 +2586,46 @@
uidState.pkgOps = new ArrayMap<>();
}
- Op op = new Op(uidState, pkgName,
- Integer.parseInt(parser.getAttributeValue(null, "n")));
+ int code = Integer.parseInt(parser.getAttributeValue(null, "n"));
+ // use op name string if it exists
+ String codeNameStr = parser.getAttributeValue(null, "ns");
+ if (codeNameStr != null) {
+ // returns OP_NONE if it could not be mapped
+ code = AppOpsManager.nameToOp(codeNameStr);
+ }
+ // skip op codes that are out of bounds
+ if (code == AppOpsManager.OP_NONE || code >= AppOpsManager._NUM_OP) {
+ continue;
+ }
+
+ Op op = new Op(uidState, pkgName, code, AppOpsManager.MODE_ERRORED);
+
+ String mode = parser.getAttributeValue(null, "m");
+ if (mode != null) {
+ op.mode = Integer.parseInt(mode);
+ } else {
+ String defaultMode = parser.getAttributeValue(null, "dm");
+ if (defaultMode != null) {
+ op.mode = Integer.parseInt(defaultMode);
+ } else {
+ op.mode = AppOpsManager.opToDefaultMode(code);
+ }
+ }
for (int i = parser.getAttributeCount()-1; i >= 0; i--) {
final String name = parser.getAttributeName(i);
final String value = parser.getAttributeValue(i);
switch (name) {
+ case "ac":
+ op.allowedCount = Integer.parseInt(value);
+ break;
+ case "ic":
+ op.ignoredCount = Integer.parseInt(value);
+ break;
+ case "dm":
case "m":
- op.mode = Integer.parseInt(value);
+ case "n":
+ case "ns":
break;
case "d":
op.duration = Integer.parseInt(value);
@@ -2495,8 +2779,12 @@
AppOpsManager.OpEntry op = ops.get(j);
out.startTag(null, "op");
out.attribute(null, "n", Integer.toString(op.getOp()));
- if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
+ out.attribute(null, "ns", AppOpsManager.opToName(op.getOp()));
+ int defaultMode = AppOpsManager.opToDefaultMode(op.getOp());
+ if (op.getMode() != defaultMode) {
out.attribute(null, "m", Integer.toString(op.getMode()));
+ } else {
+ out.attribute(null, "dm", Integer.toString(defaultMode));
}
for (int k = 0; k < _NUM_UID_STATE; k++) {
final long time = op.getLastTimeFor(k);
@@ -2522,6 +2810,14 @@
if (proxyPackageName != null) {
out.attribute(null, "pp", proxyPackageName);
}
+ int allowed = op.getAllowedCount();
+ if (allowed != 0) {
+ out.attribute(null, "ac", Integer.toString(allowed));
+ }
+ int ignored = op.getIgnoredCount();
+ if (ignored != 0) {
+ out.attribute(null, "ic", Integer.toString(ignored));
+ }
out.endTag(null, "op");
}
out.endTag(null, "uid");
@@ -3616,6 +3912,150 @@
}
}
+ final class AskRunnable implements Runnable {
+ private final PermissionDialogReq mRequest;
+
+ public AskRunnable(PermissionDialogReq request) {
+ super();
+ mRequest = request;
+ }
+
+ @Override
+ public void run() {
+ final PermissionDialogReqQueue queue = mRequest.mOp.dialogReqQueue;
+ synchronized (AppOpsService.this) {
+ Slog.e(TAG, "Creating dialog box");
+ queue.register(AppOpsService.this, mRequest);
+ }
+ queue.showDialog();
+ }
+ }
+
+ private PermissionDialogReq askOperationLocked(int code, int uid, String packageName, Op op) {
+ PermissionDialogReq request = new PermissionDialogReq(op);
+ mHandler.post(new AskRunnable(request));
+ return request;
+ }
+
+ private void printOperationLocked(Op op, int mode, String operation) {
+ if (op != null) {
+ int switchCode = AppOpsManager.opToSwitch(op.op);
+ if (mode == AppOpsManager.MODE_IGNORED) {
+ if (DEBUG) Slog.d(TAG, operation + ": reject #" + mode + " for code "
+ + switchCode + " (" + op.op + ") uid " + op.uid + " package "
+ + op.packageName);
+ } else if (mode == AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG) Slog.d(TAG, operation + ": allowing code " + op.op + " uid "
+ + op.uid + " package " + op.packageName);
+ }
+ }
+ }
+
+ private void recordOperationLocked(int code, int uid, String packageName, int mode) {
+ Op op = getOpLocked(code, uid, packageName, false);
+ UidState uidState = getUidStateLocked(uid, false);
+ if (op != null && uidState != null) {
+ if (op.noteOpCount != 0) {
+ printOperationLocked(op, mode, "noteOperartion");
+ }
+ if (op.startOpCount != 0) {
+ printOperationLocked(op, mode, "startOperation");
+ }
+ if (mode == AppOpsManager.MODE_IGNORED) {
+ op.rejectTime[uidState.state] = System.currentTimeMillis();
+ } else if (mode == AppOpsManager.MODE_ALLOWED) {
+ if (op.noteOpCount != 0) {
+ op.time[uidState.state] = System.currentTimeMillis();
+ op.rejectTime[uidState.state] = 0;
+ }
+ if (op.startOpCount != 0) {
+ if (op.startNesting == 0) {
+ op.time[uidState.state] = System.currentTimeMillis();
+ op.rejectTime[uidState.state] = 0;
+ op.duration = -1;
+ scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
+ }
+ op.startNesting = op.startNesting + op.startOpCount;
+ while (op.clientTokens.size() != 0) {
+ IBinder clientToken = op.clientTokens.get(0);
+ ClientState client = mClients.get(clientToken);
+ if (client != null) {
+ if (client.mStartedOps != null) {
+ client.mStartedOps.add(op);
+ }
+ }
+ op.clientTokens.remove(0);
+ }
+ }
+ }
+ op.clientTokens.clear();
+ op.startOpCount = 0;
+ op.noteOpCount = 0;
+ }
+ }
+
+ public void notifyOperation(int code, int uid, String packageName, int mode, boolean remember) {
+ if (DEBUG) Slog.d(TAG, "notifyOperation("
+ + code + ", " + uid + ", " + packageName + ", " + mode + ")");
+
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
+ ArraySet<ModeCallback> repCbs = null;
+ int switchCode = AppOpsManager.opToSwitch(code);
+ boolean needsPolicyUpdate = false;
+ synchronized (this) {
+ recordOperationLocked(code, uid, packageName, mode);
+ Op op = getOpLocked(switchCode, uid, packageName, true);
+ if (op != null) {
+ // Send result to all waiting client
+ op.dialogReqQueue.dismissAndNotify(mode);
+ if (remember && op.mode != mode) {
+ op.mode = mode;
+ ArraySet<ModeCallback> cbs = mOpModeWatchers.get(switchCode);
+ if (cbs != null) {
+ if (repCbs == null) {
+ repCbs = new ArraySet<ModeCallback>();
+ }
+ repCbs.addAll(cbs);
+ }
+ cbs = mPackageModeWatchers.get(packageName);
+ if (cbs != null) {
+ if (repCbs == null) {
+ repCbs = new ArraySet<ModeCallback>();
+ }
+ repCbs.addAll(cbs);
+ }
+ if (mode == AppOpsManager.opToDefaultMode(op.op)) {
+ // If going into the default mode, prune this op
+ // if there is nothing else interesting in it.
+ pruneOp(op, uid, packageName);
+ }
+ scheduleWriteLocked();
+ }
+ }
+ UidState uidState = getUidStateLocked(uid, false);
+ if (uidState != null && uidState.pendingAskOp == code) {
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ uidState.receivedPendingAskResponse = true;
+ needsPolicyUpdate = true;
+ } else {
+ uidState.pendingAskOp = 0;
+ uidState.receivedPendingAskResponse = false;
+ }
+ }
+ }
+ if (repCbs != null) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpChanged,
+ this, repCbs, code, uid, packageName));
+ }
+ if (needsPolicyUpdate) {
+ StorageManagerInternal storageManagerInternal =
+ LocalServices.getService(StorageManagerInternal.class);
+ storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
+ }
+ }
+
private static String resolvePackageName(int uid, String packageName) {
if (uid == Process.ROOT_UID) {
return "root";
@@ -3826,4 +4266,50 @@
}
}
}
+
+ @Override
+ public boolean getPrivacyGuardSettingForPackage(int uid, String packageName) {
+ for (int op : AppOpsManager.PRIVACY_GUARD_OP_STATES) {
+ int switchOp = AppOpsManager.opToSwitch(op);
+ int mode = checkOperation(op, uid, packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_IGNORED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void setPrivacyGuardSettingForPackage(int uid, String packageName, boolean state) {
+ for (int op : AppOpsManager.PRIVACY_GUARD_OP_STATES) {
+ int switchOp = AppOpsManager.opToSwitch(op);
+ setMode(switchOp, uid, packageName, state
+ ? AppOpsManager.MODE_ASK : AppOpsManager.MODE_ALLOWED);
+ }
+ }
+
+ @Override
+ public void resetCounters() {
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ synchronized (this) {
+ for (int i = 0; i < mUidStates.size(); i++) {
+ final UidState uidState = mUidStates.valueAt(i);
+ if (uidState.pkgOps == null) {
+ continue;
+ }
+ for (Map.Entry<String, Ops> ent : uidState.pkgOps.entrySet()) {
+ String packageName = ent.getKey();
+ Ops pkgOps = ent.getValue();
+ for (int j = 0; j < pkgOps.size(); j++) {
+ Op curOp = pkgOps.valueAt(j);
+ curOp.allowedCount = 0;
+ curOp.ignoredCount = 0;
+ }
+ }
+ }
+ // ensure the counter reset persists
+ scheduleWriteLocked();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 87647ca..db8ba18 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -1079,7 +1079,7 @@
}
} else if (status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL) {
- if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
+ if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 95) {
// Solid green when full or charging and nearly full
mBatteryLight.setColor(mBatteryFullARGB);
} else {
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 1cd853f..47002f0 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -16,12 +16,14 @@
package com.android.server;
+import android.app.ActivityThread;
import android.os.Binder;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.util.Slog;
import com.android.internal.os.BinderCallsStats;
+import com.android.internal.util.DumpUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -29,13 +31,14 @@
public class BinderCallsStatsService extends Binder {
private static final String TAG = "BinderCallsStatsService";
+ private static final String SERVICE_NAME = "binder_calls_stats";
private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
= "persist.sys.binder_calls_detailed_tracking";
public static void start() {
BinderCallsStatsService service = new BinderCallsStatsService();
- ServiceManager.addService("binder_calls_stats", service);
+ ServiceManager.addService(SERVICE_NAME, service);
boolean detailedTrackingEnabled = SystemProperties.getBoolean(
PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
@@ -54,6 +57,11 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(ActivityThread.currentApplication(),
+ SERVICE_NAME, pw)) {
+ return;
+ }
+
if (args != null) {
for (final String arg : args) {
if ("-a".equals(arg)) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index aa426d3..4a60e13 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +22,7 @@
import android.Manifest;
import android.app.ActivityManager;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
@@ -883,6 +887,12 @@
+ mBinding + " mState = " + BluetoothAdapter.nameForState(mState));
}
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+ if (appOps.noteOp(AppOpsManager.OP_BLUETOOTH_CHANGE, callingUid, packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+
synchronized (mReceiver) {
mQuietEnableExternal = false;
mEnableExternal = true;
diff --git a/services/core/java/com/android/server/PermissionDialog.java b/services/core/java/com/android/server/PermissionDialog.java
new file mode 100644
index 0000000..94af32f
--- /dev/null
+++ b/services/core/java/com/android/server/PermissionDialog.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2006 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;
+
+import android.app.AlertDialog;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+public class PermissionDialog extends AlertDialog {
+ private final static String TAG = "PermissionDialog";
+
+ private final AppOpsService mService;
+ private final String mPackageName;
+ private final int mCode;
+ private View mView;
+ private CheckBox mChoice;
+ private int mUid;
+ final CharSequence[] mOpLabels;
+ private Context mContext;
+ private boolean mConsuming;
+
+ // Event 'what' codes
+ private static final int MSG_START = 1;
+ private static final int MSG_ALLOWED = 2;
+ private static final int MSG_IGNORED = 3;
+ private static final int MSG_IGNORED_TIMEOUT = 4;
+
+ // 15s timeout, then we automatically dismiss the permission dialog.
+ // Otherwise, it may cause watchdog timeout sometimes.
+ private static final long DISMISS_TIMEOUT = 1000 * 15 * 1;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_START) {
+ mConsuming = false;
+ setEnabled(true);
+ } else {
+ int mode;
+ boolean remember = mChoice.isChecked();
+ switch (msg.what) {
+ case MSG_ALLOWED:
+ mode = AppOpsManager.MODE_ALLOWED;
+ break;
+ case MSG_IGNORED:
+ mode = AppOpsManager.MODE_IGNORED;
+ break;
+ default:
+ mode = AppOpsManager.MODE_IGNORED;
+ remember = false;
+ break;
+ }
+ mService.notifyOperation(mCode, mUid, mPackageName, mode, remember);
+ dismiss();
+ }
+ }
+ };
+
+ public PermissionDialog(Context context, AppOpsService service,
+ int code, int uid, String packageName) {
+ super(context, com.android.internal.R.style.Theme_Dialog_AppError);
+
+ mContext = context;
+ Resources res = context.getResources();
+
+ mService = service;
+ mCode = code;
+ mPackageName = packageName;
+ mUid = uid;
+ mOpLabels = res.getTextArray(com.android.internal.R.array.app_ops_labels);
+
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+
+ setTitle(res.getString(com.android.internal.R.string.privacy_guard_dialog_title));
+ setIconAttribute(R.attr.alertDialogIcon);
+ setCancelable(false);
+
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ res.getString(com.android.internal.R.string.allow),
+ mHandler.obtainMessage(MSG_ALLOWED));
+
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ res.getString(com.android.internal.R.string.deny),
+ mHandler.obtainMessage(MSG_IGNORED));
+
+ final CharSequence appName = getAppName(mPackageName);
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.setTitle("Permission info: " + appName);
+ attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
+ | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ getWindow().setAttributes(attrs);
+
+ mView = getLayoutInflater().inflate(
+ com.android.internal.R.layout.permission_confirmation_dialog, null);
+ TextView tv = (TextView) mView.findViewById(com.android.internal.R.id.permission_text);
+ mChoice = (CheckBox) mView.findViewById(
+ com.android.internal.R.id.permission_remember_choice_checkbox);
+ tv.setText(mContext.getString(com.android.internal.R.string.privacy_guard_dialog_summary,
+ appName, mOpLabels[mCode]));
+ setView(mView);
+
+ // After the timeout, pretend the user clicked the quit button
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_IGNORED_TIMEOUT), DISMISS_TIMEOUT);
+ }
+
+ public void ignore() {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_IGNORED_TIMEOUT));
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ setEnabled(false);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_START));
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mConsuming) {
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ private CharSequence getAppName(String packageName) {
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ ApplicationInfo appInfo = pm.getApplicationInfo(packageName,
+ PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_UNINSTALLED_PACKAGES);
+ return pm.getApplicationLabel(appInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ // fall through to returning package name
+ }
+ return packageName;
+ }
+
+ private void setEnabled(boolean enabled) {
+ View pos = getButton(DialogInterface.BUTTON_POSITIVE);
+ pos.setEnabled(enabled);
+
+ View neg = getButton(DialogInterface.BUTTON_NEGATIVE);
+ neg.setEnabled(enabled);
+ }
+}
diff --git a/services/core/java/com/android/server/PermissionDialogReqQueue.java b/services/core/java/com/android/server/PermissionDialogReqQueue.java
new file mode 100644
index 0000000..be72286
--- /dev/null
+++ b/services/core/java/com/android/server/PermissionDialogReqQueue.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2006 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;
+
+import android.app.ActivityThread;
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PermissionDialogReqQueue {
+ public final static class PermissionDialogReq {
+ boolean mHasResult = false;
+ int mResult;
+ final AppOpsService.Op mOp;
+
+ public PermissionDialogReq(final AppOpsService.Op op) {
+ mOp = op;
+ }
+
+ public void set(int res) {
+ synchronized (this) {
+ mHasResult = true;
+ mResult = res;
+ notifyAll();
+ }
+ }
+
+ public int get() {
+ synchronized (this) {
+ while (!mHasResult) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ return mResult;
+ }
+
+ }
+
+ private PermissionDialog mDialog = null;
+ private List<PermissionDialogReq> resultList = new ArrayList<>();
+
+ public void register(AppOpsService service, PermissionDialogReq req) {
+ synchronized (this) {
+ resultList.add(req);
+ }
+ if (mDialog == null) {
+ final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
+ mDialog = new PermissionDialog(context, service,
+ req.mOp.op, req.mOp.uid, req.mOp.packageName);
+ }
+ }
+
+ public void showDialog() {
+ if (mDialog != null) {
+ mDialog.show();
+ }
+ }
+
+ public void dismissAndNotify(int mode) {
+ if (mDialog == null) {
+ return;
+ }
+ synchronized (this) {
+ while (resultList.size() != 0) {
+ PermissionDialogReq res = resultList.get(0);
+ res.set(mode);
+ resultList.remove(0);
+ }
+ }
+ if (mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ mDialog = null;
+ }
+
+ public void ignore() {
+ if (mDialog != null) {
+ mDialog.ignore();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 183be9b..6c2903f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -81,6 +81,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -2254,6 +2255,9 @@
}
}, DateUtils.SECOND_IN_MILLIS);
return 0;
+ } catch (ServiceSpecificException e) {
+ Slog.e(TAG, "fdeCheckPassword failed", e);
+ return e.errorCode;
} catch (Exception e) {
Slog.wtf(TAG, e);
return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index edbff46..5a4d166 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1936,6 +1936,9 @@
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
+ static final int POST_PRIVACY_NOTIFICATION_MSG = 90;
+ static final int CANCEL_PRIVACY_NOTIFICATION_MSG = 91;
+
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
static final int FIRST_COMPAT_MODE_MSG = 300;
@@ -2557,6 +2560,67 @@
// it is finished we make sure it is reset to its default.
mUserIsMonkey = false;
} break;
+ case POST_PRIVACY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ ActivityRecord root = (ActivityRecord) msg.obj;
+ ProcessRecord process = root.app;
+ if (process == null) {
+ return;
+ }
+
+ try {
+ Context context = mContext.createPackageContext(process.info.packageName, 0);
+ String text = mContext.getString(R.string.privacy_guard_notification_detail,
+ context.getApplicationInfo().loadLabel(context.getPackageManager()));
+ String title = mContext.getString(R.string.privacy_guard_notification);
+
+ Intent infoIntent = new Intent(Settings.ACTION_APP_OPS_DETAILS_SETTINGS,
+ Uri.fromParts("package", root.packageName, null));
+
+ Notification.Builder builder = new Notification.Builder(mContext,
+ SystemNotificationChannels.SECURITY);
+ builder.setSmallIcon(com.android.internal.R.drawable.stat_notify_privacy_guard)
+ .setOngoing(true)
+ .setPriority(Notification.PRIORITY_LOW)
+ .setContentTitle(title)
+ .setContentText(text)
+ .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
+ infoIntent, PendingIntent.FLAG_CANCEL_CURRENT, null,
+ new UserHandle(root.userId)));
+ Notification notification = builder.build();
+
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotificationWithTag("android", "android", null,
+ R.string.privacy_guard_notification,
+ notification, root.userId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error showing notification for privacy guard", e);
+ } catch (RemoteException e) {
+ }
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unable to create context for privacy guard notification", e);
+ }
+ } break;
+ case CANCEL_PRIVACY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotificationWithTag("android", null,
+ R.string.privacy_guard_notification, msg.arg1);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ } break;
case IDLE_UIDS_MSG: {
idleUids();
} break;
@@ -3014,7 +3078,7 @@
mActivityStartController = null;
mAppErrors = null;
mAppWarnings = null;
- mAppOpsService = mInjector.getAppOpsService(null, null);
+ mAppOpsService = mInjector.getAppOpsService(null, null, this);
mBatteryStatsService = null;
mCompatModePackages = null;
mConstants = null;
@@ -3106,7 +3170,8 @@
mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
- mAppOpsService = mInjector.getAppOpsService(new File(systemDir, "appops.xml"), mHandler);
+ mAppOpsService = mInjector.getAppOpsService(new File(systemDir, "appops.xml"), mHandler,
+ this);
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
@@ -23093,6 +23158,7 @@
// The process is being computed, so there is a cycle. We cannot
// rely on this process's state.
app.containsCycle = true;
+
return false;
}
}
@@ -23117,6 +23183,7 @@
final int logUid = mCurOomAdjUid;
int prevAppAdj = app.curAdj;
+ int prevProcState = app.curProcState;
if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
// The max adjustment doesn't allow this app to be anything
@@ -23595,11 +23662,16 @@
ProcessRecord client = cr.binding.client;
computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
if (client.containsCycle) {
- // We've detected a cycle. We should ignore this connection and allow
- // this process to retry computeOomAdjLocked later in case a later-checked
- // connection from a client would raise its priority legitimately.
+ // We've detected a cycle. We should retry computeOomAdjLocked later in
+ // case a later-checked connection from a client would raise its
+ // priority legitimately.
app.containsCycle = true;
- continue;
+ // If the client has not been completely evaluated, skip using its
+ // priority. Else use the conservative value for now and look for a
+ // better state in the next iteration.
+ if (client.completedAdjSeq < mAdjSeq) {
+ continue;
+ }
}
int clientAdj = client.curRawAdj;
int clientProcState = client.curProcState;
@@ -23822,11 +23894,16 @@
}
computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
if (client.containsCycle) {
- // We've detected a cycle. We should ignore this connection and allow
- // this process to retry computeOomAdjLocked later in case a later-checked
- // connection from a client would raise its priority legitimately.
+ // We've detected a cycle. We should retry computeOomAdjLocked later in
+ // case a later-checked connection from a client would raise its
+ // priority legitimately.
app.containsCycle = true;
- continue;
+ // If the client has not been completely evaluated, skip using its
+ // priority. Else use the conservative value for now and look for a
+ // better state in the next iteration.
+ if (client.completedAdjSeq < mAdjSeq) {
+ continue;
+ }
}
int clientAdj = client.curRawAdj;
int clientProcState = client.curProcState;
@@ -24058,8 +24135,8 @@
app.foregroundActivities = foregroundActivities;
app.completedAdjSeq = mAdjSeq;
- // if curAdj is less than prevAppAdj, then this process was promoted
- return app.curAdj < prevAppAdj;
+ // if curAdj or curProcState improved, then this process was promoted
+ return app.curAdj < prevAppAdj || app.curProcState < prevProcState;
}
/**
@@ -25112,7 +25189,7 @@
// - Continue retrying until no process was promoted.
// - Iterate from least important to most important.
int cycleCount = 0;
- while (retryCycles) {
+ while (retryCycles && cycleCount < 10) {
cycleCount++;
retryCycles = false;
@@ -25127,12 +25204,14 @@
for (int i=0; i<N; i++) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
+
if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
retryCycles = true;
}
}
}
}
+
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
@@ -27153,8 +27232,9 @@
return null;
}
- public AppOpsService getAppOpsService(File file, Handler handler) {
- return new AppOpsService(file, handler);
+ public AppOpsService getAppOpsService(File file, Handler handler,
+ ActivityManagerService service) {
+ return new AppOpsService(file, handler, service);
}
public Handler getUiHandler(ActivityManagerService service) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 75f2723..fafb4a4 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1888,6 +1888,31 @@
// pause and then resume again later, which will result in a double life-cycle event.
stack.checkReadyForSleep();
}
+
+ service.mAppOpsService.handlePackageResumed(this.app.uid, this.packageName);
+ updatePrivacyGuardNotificationLocked();
+ }
+
+ private final void updatePrivacyGuardNotificationLocked() {
+ String privacyGuardPackageName = mStackSupervisor.mPrivacyGuardPackageName;
+ if (privacyGuardPackageName != null && privacyGuardPackageName.equals(this.packageName)) {
+ return;
+ }
+
+ boolean privacy = service.mAppOpsService.getPrivacyGuardSettingForPackage(
+ this.app.uid, this.packageName);
+
+ if (privacyGuardPackageName != null && !privacy) {
+ Message msg = service.mHandler.obtainMessage(
+ ActivityManagerService.CANCEL_PRIVACY_NOTIFICATION_MSG, this.userId);
+ msg.sendToTarget();
+ mStackSupervisor.mPrivacyGuardPackageName = null;
+ } else if (privacy && false) {
+ Message msg = service.mHandler.obtainMessage(
+ ActivityManagerService.POST_PRIVACY_NOTIFICATION_MSG, this);
+ msg.sendToTarget();
+ mStackSupervisor.mPrivacyGuardPackageName = this.packageName;
+ }
}
final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 3b3ff00..90f0694 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -394,6 +394,9 @@
/** Set when a power hint has started, but not ended. */
private boolean mPowerHintSent;
+ /** Is Privacy Guard currently enabled? Shared between ActivityStacks. */
+ String mPrivacyGuardPackageName = null;
+
/**
* We don't want to allow the device to go to sleep while in the process
* of launching an activity. This is primarily to allow alarm intent
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 18c0957..be3c4f1 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -357,7 +357,7 @@
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
+ AppOpsManager.OP_BOOT_COMPLETED, null, true, false, MY_PID, SYSTEM_UID, userId);
}
// We need to delay unlocking managed profiles until the parent user
@@ -554,7 +554,7 @@
}
}, 0, null, null,
new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
+ AppOpsManager.OP_BOOT_COMPLETED, null, true, false, MY_PID, SYSTEM_UID, userId);
}
int restartUser(final int userId, final boolean foreground) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 13de652..ecf17ff 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -409,7 +409,7 @@
* e.g. user on homescreen, no app playing anything, presses hardware volume buttons, this
* stream type is controlled.
*/
- protected static final int DEFAULT_VOL_STREAM_NO_PLAYBACK = AudioSystem.STREAM_MUSIC;
+ protected static final int DEFAULT_VOL_STREAM_NO_PLAYBACK = AudioSystem.STREAM_RING;
private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
public void onError(int error) {
@@ -4400,10 +4400,10 @@
return AudioSystem.STREAM_VOICE_CALL;
}
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
+ if (wasStreamActiveRecently(AudioSystem.STREAM_MUSIC, sStreamOverrideDelayMs)) {
if (DEBUG_VOL)
- Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
- return AudioSystem.STREAM_RING;
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
+ return AudioSystem.STREAM_MUSIC;
} else if (wasStreamActiveRecently(
AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL)
@@ -4450,9 +4450,9 @@
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
} else if (AudioSystem.isStreamActive(
- AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
- if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
- return AudioSystem.STREAM_RING;
+ AudioSystem.STREAM_MUSIC, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC");
+ return AudioSystem.STREAM_MUSIC;
} else {
if (DEBUG_VOL) {
Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5db20b0..daf0655 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -152,6 +152,11 @@
private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
+ /* AppOps check variants for the clipboardAccessAllowed method */
+ private static final int APPOP_NOTE = 1; /** Call AppOps.noteOp method */
+ private static final int APPOP_CHECK = 2; /** Call AppOps.checkOp method */
+ private static final int APPOP_NOTHROW = 3; /** Call AppOps.checkOpNoThrow method */
+
/**
* Instantiates the clipboard.
*/
@@ -252,7 +257,7 @@
}
final int callingUid = Binder.getCallingUid();
if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
- callingUid)) {
+ callingUid, APPOP_NOTE)) {
return;
}
checkDataOwnerLocked(clip, callingUid);
@@ -265,7 +270,7 @@
synchronized (this) {
final int callingUid = Binder.getCallingUid();
if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
- callingUid)) {
+ callingUid, APPOP_NOTHROW)) {
return;
}
setPrimaryClipInternal(null, callingUid);
@@ -276,7 +281,7 @@
public ClipData getPrimaryClip(String pkg) {
synchronized (this) {
if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, pkg,
- Binder.getCallingUid()) || isDeviceLocked()) {
+ Binder.getCallingUid(), APPOP_NOTE) || isDeviceLocked()) {
return null;
}
addActiveOwnerLocked(Binder.getCallingUid(), pkg);
@@ -288,7 +293,7 @@
public ClipDescription getPrimaryClipDescription(String callingPackage) {
synchronized (this) {
if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- Binder.getCallingUid()) || isDeviceLocked()) {
+ Binder.getCallingUid(), APPOP_CHECK) || isDeviceLocked()) {
return null;
}
PerUserClipboard clipboard = getClipboard();
@@ -300,7 +305,7 @@
public boolean hasPrimaryClip(String callingPackage) {
synchronized (this) {
if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- Binder.getCallingUid()) || isDeviceLocked()) {
+ Binder.getCallingUid(), APPOP_CHECK) || isDeviceLocked()) {
return false;
}
return getClipboard().primaryClip != null;
@@ -327,7 +332,7 @@
public boolean hasClipboardText(String callingPackage) {
synchronized (this) {
if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- Binder.getCallingUid()) || isDeviceLocked()) {
+ Binder.getCallingUid(), APPOP_CHECK) || isDeviceLocked()) {
return false;
}
PerUserClipboard clipboard = getClipboard();
@@ -464,7 +469,7 @@
clipboard.primaryClipListeners.getBroadcastCookie(i);
if (clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, li.mPackageName,
- li.mUid)) {
+ li.mUid, APPOP_NOTHROW)) {
clipboard.primaryClipListeners.getBroadcastItem(i)
.dispatchPrimaryClipChanged();
}
@@ -619,9 +624,24 @@
}
}
- private boolean clipboardAccessAllowed(int op, String callingPackage, int callingUid) {
- // Check the AppOp.
- if (mAppOps.noteOp(op, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ private boolean clipboardAccessAllowed(int op, String callingPackage,
+ int callingUid, int appOpMethod) {
+ int appOpResult;
+
+ // Check the AppOp depending on the specified method.
+ switch (appOpMethod) {
+ case APPOP_NOTE:
+ appOpResult = mAppOps.noteOp(op, callingUid, callingPackage);
+ break;
+ case APPOP_NOTHROW:
+ appOpResult = mAppOps.checkOpNoThrow(op, callingUid, callingPackage);
+ break;
+ default:
+ appOpResult = mAppOps.checkOp(op, callingUid, callingPackage);
+ break;
+ }
+
+ if (appOpResult != AppOpsManager.MODE_ALLOWED) {
return false;
}
try {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 73eeda3..dd0879b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -332,6 +332,8 @@
private long[] mFallbackVibrationPattern;
private boolean mUseAttentionLight;
+ boolean mHasLight = true;
+ boolean mLightEnabled;
boolean mSystemReady;
private boolean mDisableNotificationEffects;
@@ -346,9 +348,9 @@
private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
// for enabling and disabling notification pulse behavior
- private boolean mScreenOn = true;
+ boolean mScreenOn = true;
protected boolean mInCall = false;
- private boolean mNotificationPulseEnabled;
+ boolean mNotificationPulseEnabled;
private Uri mInCallNotificationUri;
private AudioAttributes mInCallNotificationAudioAttributes;
@@ -1217,7 +1219,8 @@
ContentResolver resolver = getContext().getContentResolver();
if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
boolean pulseEnabled = Settings.System.getIntForUser(resolver,
- Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT)
+ != 0;
if (mNotificationPulseEnabled != pulseEnabled) {
mNotificationPulseEnabled = pulseEnabled;
updateNotificationPulse();
@@ -1485,6 +1488,8 @@
mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
+ mHasLight =
+ resources.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed);
// Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on.
@@ -3881,6 +3886,7 @@
pw.println(" ");
}
pw.println(" mUseAttentionLight=" + mUseAttentionLight);
+ pw.println(" mHasLight=" + mHasLight);
pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
@@ -4578,6 +4584,15 @@
@GuardedBy("mNotificationLock")
@VisibleForTesting
protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
+ // Ignore summary updates because we don't display most of the information.
+ if (r.sbn.isGroup() && r.sbn.getNotification().isGroupSummary()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is not interruptive: summary");
+ }
+ return false;
+ }
+
if (old == null) {
if (DEBUG_INTERRUPTIVENESS) {
Log.v(TAG, "INTERRUPTIVENESS: "
@@ -4615,15 +4630,6 @@
return false;
}
- // Ignore summary updates because we don't display most of the information.
- if (r.sbn.isGroup() && r.sbn.getNotification().isGroupSummary()) {
- if (DEBUG_INTERRUPTIVENESS) {
- Log.v(TAG, "INTERRUPTIVENESS: "
- + r.getKey() + " is not interruptive: summary");
- }
- return false;
- }
-
final String oldTitle = String.valueOf(oldN.extras.get(Notification.EXTRA_TITLE));
final String newTitle = String.valueOf(newN.extras.get(Notification.EXTRA_TITLE));
if (!Objects.equals(oldTitle, newTitle)) {
@@ -4878,8 +4884,7 @@
// light
// release the light
boolean wasShowLights = mLights.remove(key);
- if (record.getLight() != null && aboveThreshold
- && ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) == 0)) {
+ if (canShowLightsLocked(record, aboveThreshold)) {
mLights.add(key);
updateLightsLocked();
if (mUseAttentionLight) {
@@ -4890,7 +4895,19 @@
updateLightsLocked();
}
if (buzz || beep || blink) {
- record.setInterruptive(true);
+ // Ignore summary updates because we don't display most of the information.
+ if (record.sbn.isGroup() && record.sbn.getNotification().isGroupSummary()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + record.getKey() + " is not interruptive: summary");
+ }
+ } else {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + record.getKey() + " is interruptive: alerted");
+ }
+ record.setInterruptive(true);
+ }
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
.setType(MetricsEvent.TYPE_OPEN)
@@ -4900,11 +4917,49 @@
}
@GuardedBy("mNotificationLock")
+ boolean canShowLightsLocked(final NotificationRecord record, boolean aboveThreshold) {
+ // device lacks light
+ if (!mHasLight) {
+ return false;
+ }
+ // user turned lights off globally
+ if (!mNotificationPulseEnabled) {
+ return false;
+ }
+ // the notification/channel has no light
+ if (record.getLight() == null) {
+ return false;
+ }
+ // unimportant notification
+ if (!aboveThreshold) {
+ return false;
+ }
+ // suppressed due to DND
+ if ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) != 0) {
+ return false;
+ }
+ // Suppressed because it's a silent update
+ final Notification notification = record.getNotification();
+ if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+ return false;
+ }
+ // Suppressed because another notification in its group handles alerting
+ if (record.sbn.isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
+ return false;
+ }
+ // not if in call or the screen's on
+ if (mInCall || mScreenOn) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @GuardedBy("mNotificationLock")
boolean shouldMuteNotificationLocked(final NotificationRecord record) {
// Suppressed because it's a silent update
final Notification notification = record.getNotification();
- if(record.isUpdate
- && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+ if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
return true;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5306b7a..16f167a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1274,6 +1274,8 @@
filter.hasDataScheme(IntentFilter.SCHEME_HTTPS));
}
+ private AppOpsManager mAppOps;
+
// Set of pending broadcasts for aggregating enable/disable of components.
static class PendingPackageBroadcasts {
// for each user id, a map of <package name -> components within that package>
@@ -2191,6 +2193,11 @@
notifyPackageAdded(packageName);
}
+ if (!update && !isSystemApp(res.pkg)) {
+ // mAppOps.setPrivacyGuardSettingForPackage(res.pkg.applicationInfo.uid,
+ // res.pkg.applicationInfo.packageName, true);
+ }
+
// Log current value of "unknown sources" setting
EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
getUnknownSourcesSettings());
@@ -2490,6 +2497,8 @@
mSettings.addSharedUserLPw("android.uid.se", SE_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ mAppOps = context.getSystemService(AppOpsManager.class);
+
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
@@ -21269,6 +21278,9 @@
// disabled after already being started.
CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
mContext.getContentResolver(), UserHandle.USER_SYSTEM);
+ // And also disable any special carrier apps.
+ CarrierAppUtils.disableSpecialCarrierAppsUntilMatched(mContext, this,
+ UserHandle.USER_SYSTEM);
// Read the compatibilty setting when the system is ready.
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 06c56a0..cfe6261 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1358,6 +1358,7 @@
mGuestRestrictions.putBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true);
+ mGuestRestrictions.putBoolean(UserManager.DISALLOW_SU, true);
}
}
}
@@ -3878,6 +3879,7 @@
if (user != null && !user.isAdmin() && !user.isDemo()) {
setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, true, user.id);
+ setUserRestriction(UserManager.DISALLOW_SU, true, user.id);
}
return user;
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 9ca02ba..975545d 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -126,7 +126,8 @@
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
UserManager.DISALLOW_AMBIENT_DISPLAY,
UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT,
- UserManager.DISALLOW_PRINTING
+ UserManager.DISALLOW_PRINTING,
+ UserManager.DISALLOW_SU
});
/**
@@ -155,7 +156,8 @@
UserManager.DISALLOW_SAFE_BOOT,
UserManager.DISALLOW_CREATE_WINDOWS,
UserManager.DISALLOW_DATA_ROAMING,
- UserManager.DISALLOW_AIRPLANE_MODE
+ UserManager.DISALLOW_AIRPLANE_MODE,
+ UserManager.DISALLOW_SU
);
/**
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 0a6b38f..7d3d9b3 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -533,6 +533,7 @@
shutdownTimingLog.traceEnd(); // SystemServerShutdown
metricEnded(METRIC_SYSTEM_SERVER);
saveMetrics(mReboot, mReason);
+ registerPlannedShutdown();
// Remaining work will be done by init, including vold shutdown
rebootOrShutdown(mContext, mReboot, mReason);
}
@@ -709,6 +710,10 @@
}
}
+ private static void registerPlannedShutdown() {
+ SystemProperties.set("persist.runtime.shutdown_planned", "1");
+ }
+
private void uncrypt() {
Log.i(TAG, "Calling uncrypt and monitoring the progress...");
diff --git a/services/core/java/com/android/server/slice/SliceClientPermissions.java b/services/core/java/com/android/server/slice/SliceClientPermissions.java
index e461e0d..e241205 100644
--- a/services/core/java/com/android/server/slice/SliceClientPermissions.java
+++ b/services/core/java/com/android/server/slice/SliceClientPermissions.java
@@ -160,6 +160,9 @@
// Get to the beginning of the provider.
while (parser.getEventType() != XmlPullParser.START_TAG
|| !TAG_CLIENT.equals(parser.getName())) {
+ if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+ throw new XmlPullParserException("Can't find client tag in xml");
+ }
parser.next();
}
int depth = parser.getDepth();
@@ -173,6 +176,9 @@
parser.next();
while (parser.getDepth() > depth) {
+ if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+ return provider;
+ }
if (parser.getEventType() == XmlPullParser.START_TAG
&& TAG_AUTHORITY.equals(parser.getName())) {
try {
@@ -282,9 +288,12 @@
public synchronized void writeTo(XmlSerializer out) throws IOException {
final int N = mPaths.size();
for (int i = 0; i < N; i++) {
- out.startTag(NAMESPACE, TAG_PATH);
- out.text(encodeSegments(mPaths.valueAt(i)));
- out.endTag(NAMESPACE, TAG_PATH);
+ final String[] segments = mPaths.valueAt(i);
+ if (segments != null) {
+ out.startTag(NAMESPACE, TAG_PATH);
+ out.text(encodeSegments(segments));
+ out.endTag(NAMESPACE, TAG_PATH);
+ }
}
}
diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java
index 780bc96..343d2e3 100644
--- a/services/core/java/com/android/server/slice/SlicePermissionManager.java
+++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java
@@ -130,7 +130,7 @@
}
SliceClientPermissions client = getClient(pkgUser);
client.clear();
- mHandler.obtainMessage(H.MSG_REMOVE, pkgUser);
+ mHandler.obtainMessage(H.MSG_REMOVE, pkgUser).sendToTarget();
}
public String[] getAllPackagesGranted(String pkg) {
@@ -175,18 +175,24 @@
handlePersist();
}
for (String file : new File(mSliceDir.getAbsolutePath()).list()) {
- if (file.isEmpty()) continue;
try (ParserHolder parser = getParser(file)) {
- Persistable p;
- while (parser.parser.getEventType() != XmlPullParser.START_TAG) {
+ Persistable p = null;
+ while (parser.parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if (parser.parser.getEventType() == XmlPullParser.START_TAG) {
+ if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) {
+ p = SliceClientPermissions.createFrom(parser.parser, tracker);
+ } else {
+ p = SliceProviderPermissions.createFrom(parser.parser, tracker);
+ }
+ break;
+ }
parser.parser.next();
}
- if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) {
- p = SliceClientPermissions.createFrom(parser.parser, tracker);
+ if (p != null) {
+ p.writeTo(out);
} else {
- p = SliceProviderPermissions.createFrom(parser.parser, tracker);
+ Slog.w(TAG, "Invalid or empty slice permissions file: " + file);
}
- p.writeTo(out);
}
}
@@ -315,7 +321,8 @@
return new AtomicFile(new File(mSliceDir, fileName));
}
- private void handlePersist() {
+ @VisibleForTesting
+ void handlePersist() {
synchronized (this) {
for (Persistable persistable : mDirty) {
AtomicFile file = getFile(persistable.getFileName());
@@ -335,7 +342,7 @@
out.flush();
file.finishWrite(stream);
- } catch (IOException | XmlPullParserException e) {
+ } catch (IOException | XmlPullParserException | RuntimeException e) {
Slog.w(TAG, "Failed to save access file, restoring backup", e);
file.failWrite(stream);
}
@@ -344,6 +351,12 @@
}
}
+ // use addPersistableDirty(); this is just for tests
+ @VisibleForTesting
+ void addDirtyImmediate(Persistable obj) {
+ mDirty.add(obj);
+ }
+
private void handleRemove(PkgUser pkgUser) {
getFile(SliceClientPermissions.getFileName(pkgUser)).delete();
getFile(SliceProviderPermissions.getFileName(pkgUser)).delete();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1880e9f..2f9dc29 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -450,6 +450,8 @@
}
}
+ checkLastShutdownWasClean();
+
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
@@ -1942,4 +1944,18 @@
private static void traceEnd() {
BOOT_TIMINGS_TRACE_LOG.traceEnd();
}
+
+ private static void checkLastShutdownWasClean() {
+ // On clean (planned) shutdown, the ShutdownThread sets persist.runtime.shutdown_planned to
+ // true. On initial boot it's unset; so we consider it to be a clean boot.
+ boolean lastShutdownWasClean =
+ SystemProperties.getBoolean("persist.runtime.shutdown_planned", true);
+
+ // Don't make this a read-only property: A runtime crash is considered a "unclean shutdown",
+ // so we would then set it to 0.
+ SystemProperties.set("runtime.last_shutdown_was_clean",
+ lastShutdownWasClean ? "1" : "0");
+
+ SystemProperties.set("persist.runtime.shutdown_planned", "0");
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index e7b3686..54964cc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -19,7 +19,9 @@
import static android.app.Notification.GROUP_ALERT_CHILDREN;
import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
@@ -156,6 +158,9 @@
mService.setUsageStats(mUsageStats);
mService.setAccessibilityManager(accessibilityManager);
mService.setKeyguardManager(mKeyguardManager);
+ mService.mScreenOn = false;
+ mService.mInCall = false;
+ mService.mNotificationPulseEnabled = true;
}
//
@@ -223,8 +228,13 @@
}
private NotificationRecord getLightsNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ false /* noisy */, false /* buzzy*/, true /* lights */);
+ }
+
+ private NotificationRecord getLightsOnceNotification() {
return getNotificationRecord(mId, false /* insistent */, true /* once */,
- false /* noisy */, true /* buzzy*/, true /* lights */);
+ false /* noisy */, false /* buzzy*/, true /* lights */);
}
private NotificationRecord getCustomLightsNotification() {
@@ -251,6 +261,12 @@
groupKey, groupAlertBehavior, false);
}
+ private NotificationRecord getLightsNotificationRecord(String groupKey,
+ int groupAlertBehavior) {
+ return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, true,
+ true, groupKey, groupAlertBehavior, false);
+ }
+
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
@@ -376,6 +392,10 @@
verify(mVibrator, never()).cancel();
}
+ private void verifyNeverLights() {
+ verify(mLight, never()).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
private void verifyLights() {
verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -807,7 +827,8 @@
mService.buzzBeepBlinkLocked(summary);
verifyBeepLooped();
- assertTrue(summary.isInterruptive());
+ // summaries are never interruptive for notification counts
+ assertFalse(summary.isInterruptive());
}
@Test
@@ -1085,6 +1106,156 @@
verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
}
+ @Test
+ public void testLightsScreenOn() {
+ mService.mScreenOn = true;
+ NotificationRecord r = getLightsNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsInCall() {
+ mService.mInCall = true;
+ NotificationRecord r = getLightsNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsSilentUpdate() {
+ NotificationRecord r = getLightsOnceNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyLights();
+ assertTrue(r.isInterruptive());
+
+ r = getLightsOnceNotification();
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+ // checks that lights happened once, i.e. this new call didn't trigger them again
+ verifyLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsUnimportant() {
+ NotificationRecord r = getLightsNotification();
+ r.setImportance(IMPORTANCE_LOW, "testing");
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsNoLights() {
+ NotificationRecord r = getQuietNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsNoLightOnDevice() {
+ mService.mHasLight = false;
+ NotificationRecord r = getLightsNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsLightsOffGlobally() {
+ mService.mNotificationPulseEnabled = false;
+ NotificationRecord r = getLightsNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsDndIntercepted() {
+ NotificationRecord r = getLightsNotification();
+ r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_LIGHTS);
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertSummaryNoLightsChild() {
+ NotificationRecord child = getLightsNotificationRecord("a", GROUP_ALERT_SUMMARY);
+
+ mService.buzzBeepBlinkLocked(child);
+
+ verifyNeverLights();
+ assertFalse(child.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertSummaryLightsSummary() {
+ NotificationRecord summary = getLightsNotificationRecord("a", GROUP_ALERT_SUMMARY);
+ summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+ mService.buzzBeepBlinkLocked(summary);
+
+ verifyLights();
+ // summaries should never count for interruptiveness counts
+ assertFalse(summary.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertSummaryLightsNonGroupChild() {
+ NotificationRecord nonGroup = getLightsNotificationRecord(null, GROUP_ALERT_SUMMARY);
+
+ mService.buzzBeepBlinkLocked(nonGroup);
+
+ verifyLights();
+ assertTrue(nonGroup.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertChildNoLightsSummary() {
+ NotificationRecord summary = getLightsNotificationRecord("a", GROUP_ALERT_CHILDREN);
+ summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+ mService.buzzBeepBlinkLocked(summary);
+
+ verifyNeverLights();
+ assertFalse(summary.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertChildLightsChild() {
+ NotificationRecord child = getLightsNotificationRecord("a", GROUP_ALERT_CHILDREN);
+
+ mService.buzzBeepBlinkLocked(child);
+
+ verifyLights();
+ assertTrue(child.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertChildLightsNonGroupSummary() {
+ NotificationRecord nonGroup = getLightsNotificationRecord(null, GROUP_ALERT_CHILDREN);
+
+ mService.buzzBeepBlinkLocked(nonGroup);
+
+ verifyLights();
+ assertTrue(nonGroup.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertAllLightsGroup() {
+ NotificationRecord group = getLightsNotificationRecord("a", GROUP_ALERT_ALL);
+
+ mService.buzzBeepBlinkLocked(group);
+
+ verifyLights();
+ assertTrue(group.isInterruptive());
+ }
+
static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
private final int mRepeatIndex;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index d47e343..eff4eb7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2988,6 +2988,20 @@
}
@Test
+ public void testVisualDifference_summaryNewNotification() {
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setGroup("bananas")
+ .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+ .setContentText("bar");
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertFalse(mService.isVisuallyInterruptive(null, r2));
+ }
+
+ @Test
public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() {
// post 2 notification from this package
final NotificationRecord notif1 = generateNotificationRecord(
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
index dc057d5..efefee1 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
@@ -16,6 +16,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -26,6 +27,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.Log;
import android.util.Xml.Encoding;
import com.android.server.UiServiceTestCase;
@@ -46,10 +48,12 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class SlicePermissionManagerTest extends UiServiceTestCase {
+ private static final String TAG = "SlicePerManTest";
@Test
public void testGrant() {
- File sliceDir = new File(mContext.getDataDir(), "system/slices");
+ File sliceDir = new File(mContext.getCacheDir(), "testGrantSlices");
+ Log.v(TAG, "testGrant: slice permissions stored in " + sliceDir.getAbsolutePath());
SlicePermissionManager permissions = new SlicePermissionManager(mContext,
TestableLooper.get(this).getLooper(), sliceDir);
Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT)
@@ -59,11 +63,15 @@
permissions.grantSliceAccess("my.pkg", 0, "provider.pkg", 0, uri);
assertTrue(permissions.hasPermission("my.pkg", 0, uri));
+
+ // Cleanup.
+ assertTrue(FileUtils.deleteContentsAndDir(sliceDir));
}
@Test
public void testBackup() throws XmlPullParserException, IOException {
- File sliceDir = new File(mContext.getDataDir(), "system/slices");
+ File sliceDir = new File(mContext.getCacheDir(), "testBackupSlices");
+ Log.v(TAG, "testBackup: slice permissions stored in " + sliceDir.getAbsolutePath());
Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority("authority")
.path("something").build();
@@ -90,7 +98,10 @@
TestableLooper.get(this).getLooper());
permissions.readRestore(parser);
- assertTrue(permissions.hasFullAccess("com.android.mypkg", 10));
+ if (!permissions.hasFullAccess("com.android.mypkg", 10)) {
+ fail("com.android.mypkg@10 did not have full access. backup file: "
+ + output.toString());
+ }
assertTrue(permissions.hasPermission("com.android.otherpkg", 0,
ContentProvider.maybeAddUserId(uri, 1)));
permissions.removePkg("com.android.lastpkg", 1);
@@ -101,4 +112,36 @@
assertTrue(FileUtils.deleteContentsAndDir(sliceDir));
}
-}
\ No newline at end of file
+ @Test
+ public void testInvalid() {
+ File sliceDir = new File(mContext.getCacheDir(), "testInvalidSlices");
+ Log.v(TAG, "testInvalid: slice permissions stored in " + sliceDir.getAbsolutePath());
+ if (!sliceDir.exists()) {
+ sliceDir.mkdir();
+ }
+ SlicePermissionManager permissions = new SlicePermissionManager(mContext,
+ TestableLooper.get(this).getLooper(), sliceDir);
+
+ DirtyTracker.Persistable junk = new DirtyTracker.Persistable() {
+ @Override
+ public String getFileName() {
+ return "invalidData";
+ }
+
+ @Override
+ public void writeTo(XmlSerializer out) throws IOException {
+ throw new RuntimeException("this RuntimeException inside junk.writeTo() "
+ + "should be caught and suppressed by surrounding code");
+ }
+ };
+
+ // let's put something bad in here
+ permissions.addDirtyImmediate(junk);
+ // force a persist. if this throws, it would take down system_server
+ permissions.handlePersist();
+
+ // Cleanup.
+ assertTrue(FileUtils.deleteContentsAndDir(sliceDir));
+ }
+
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 9f8042c..36f8063 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -279,8 +279,8 @@
}
mControlFds.put(UsbManager.FUNCTION_MTP, mtpFd);
FileDescriptor ptpFd = nativeOpenControl(UsbManager.USB_FUNCTION_PTP);
- if (mtpFd == null) {
- Slog.e(TAG, "Failed to open control for mtp");
+ if (ptpFd == null) {
+ Slog.e(TAG, "Failed to open control for ptp");
}
mControlFds.put(UsbManager.FUNCTION_PTP, ptpFd);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 487490c..744130d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,6 +33,7 @@
import android.annotation.TestApi;
import android.annotation.WorkerThread;
import android.app.ActivityThread;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -6437,6 +6441,14 @@
public void setDataEnabled(int subId, boolean enable) {
try {
Log.d(TAG, "setDataEnabled: enabled=" + enable);
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+ if (enable) {
+ if (appOps.noteOp(AppOpsManager.OP_DATA_CONNECT_CHANGE)
+ != AppOpsManager.MODE_ALLOWED) {
+ Log.w(TAG, "Permission denied by user.");
+ return;
+ }
+ }
ITelephony telephony = getITelephony();
if (telephony != null)
telephony.setUserDataEnabled(subId, enable);
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
index bcad554..938ce8f 100644
--- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017-2020 Fairphone B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,13 +19,18 @@
import android.annotation.Nullable;
import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -33,6 +39,7 @@
import com.android.server.SystemConfig;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -45,6 +52,11 @@
private static final boolean DEBUG = false; // STOPSHIP if true
+ private static final String SPECIAL_CARRIER_APP_INTENT_SERVICE_CLASS = "com.android.phone.SpecialCarrierAppIntentService";
+ private static final String SPECIAL_CARRIER_APP_ACTION_HANDLE_MATCH = "com.android.phone.SpecialCarrierApp.handle_match";
+ private static final String SPECIAL_CARRIER_APP_EXTRA_APP_INFO = "com.android.phone.SpecialCarrierApp.app_info";
+ private static final String SPECIAL_CARRIER_APP_EXTRA_DEVICE_PROVISIONED = "com.android.phone.SpecialCarrierApp.device_provisioned";
+
private CarrierAppUtils() {}
/**
@@ -246,6 +258,182 @@
}
/**
+ * Like {@link #disableSpecialCarrierAppsUntilMatched(Context, IPackageManager,
+ * TelephonyManager, int)}, but assumes that no carrier apps have carrier privileges.
+ *
+ * This prevents a potential race condition on first boot - since the app's default state is
+ * enabled, we will initially disable it when the telephony stack is first initialized as it has
+ * not yet read the carrier privilege rules. However, since telephony is initialized later on
+ * late in boot, the app being disabled may have already been started in response to certain
+ * broadcasts. The app will continue to run (briefly) after being disabled, before the Package
+ * Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
+ */
+ public synchronized static void disableSpecialCarrierAppsUntilMatched(Context context,
+ IPackageManager packageManager, int userId) {
+ disableSpecialCarrierAppsUntilMatched(
+ context, packageManager, null /* telephonyManager */, userId);
+ }
+
+ /**
+ * Handle special carrier apps which should be disabled until a matching UICC is detected.
+ *
+ * Evaluates the list of applications in config_disabledUntilUsedSpecialCarrierApps. We
+ * want to disable each such application which is present on the system image until an UICC
+ * exposing the right operator code is found (indicating a "match"), without interfering with
+ * the user if they opt to enable/disable the app explicitly.
+ *
+ * So, for each such app, we either disable until used if -and only if- the app is not matching
+ * a carrier AND is in the default state (e.g. not explicitly enabled or disabled by the user),
+ * or we ask the adequate service (SpecialCarrierAppIntentService living in the Telephony
+ * service) to handle our request if the app is matching a carrier and in either the default
+ * state (first boot) or flagged as DISABLED_UNTIL_USED.
+ *
+ * This method differs from
+ * {@link #disableCarrierAppsUntilPrivileged(String, IPackageManager, TelephonyManager, int)} in
+ * two ways:
+ * - The apps are enabled based on the UICC operator code and the package signature; and
+ * - When enabling a carrier app, no permissions are implicitly granted.
+ * If the configuration specifies so (per app), an intents is broadcasted after a carrier app
+ * has been enabled.
+ *
+ * This method is idempotent and is safe to be called at any time; it should be called once at
+ * system startup prior to any application running, as well as any time the set of carrier
+ * privileged apps may have changed.
+ */
+ public synchronized static void disableSpecialCarrierAppsUntilMatched(Context context,
+ IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "disableSpecialCarrierAppsUntilMatched");
+ }
+
+ String[] specialSystemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+ com.android.internal.R.array.config_disabledUntilUsedSpecialCarrierApps);
+ List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
+ userId, new ArraySet<>(Arrays.asList(specialSystemCarrierAppsDisabledUntilUsed)));
+
+ Slog.d(TAG, "Found " + (candidates == null ? 0 : candidates.size()) + " candidate(s) for "
+ + specialSystemCarrierAppsDisabledUntilUsed.length + " special carrier app(s)");
+
+ if (candidates == null || candidates.isEmpty()) {
+ return;
+ }
+
+ String[] simOperatorCodes;
+ if (telephonyManager != null) {
+ simOperatorCodes = new String[telephonyManager.getPhoneCount()];
+ for (int i = 0; i < simOperatorCodes.length; i++) {
+ simOperatorCodes[i] = telephonyManager.getSimOperatorNumericForPhone(i);
+ }
+ } else {
+ simOperatorCodes = new String[0];
+ }
+
+ String[] specialSystemCarrierAppOperatorCodes = Resources.getSystem().getStringArray(
+ com.android.internal.R.array.config_disabledUntilUsedSpecialCarrierAppUiccOperators);
+ String[] specialSystemCarrierAppIntents = Resources.getSystem().getStringArray(
+ com.android.internal.R.array.config_disabledUntilUsedSpecialCarrierAppIntents);
+
+ try {
+ for (ApplicationInfo ai : candidates) {
+ String operatorCodesToMatch = null, actionIntentToBroadcast = null;
+ // Do not try to match if the telephony stack is not ready
+ boolean shouldBeMatched = telephonyManager != null;
+
+ // Only update enabled state for the app on /system. Once it has been updated we
+ // shouldn't touch it.
+ if (ai.isUpdatedSystemApp()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Special carrier app candidate '" + ai.packageName
+ + "' is an updated system app, ignoring");
+ }
+ continue;
+ }
+
+ // Not so nice look-up for every package name.
+ // The impact is mitigated by the low amount of items (a handful) expected.
+ for (int i = 0; i < specialSystemCarrierAppsDisabledUntilUsed.length; i++) {
+ if (ai.packageName.equals(specialSystemCarrierAppsDisabledUntilUsed[i])) {
+ operatorCodesToMatch = specialSystemCarrierAppOperatorCodes[i];
+ actionIntentToBroadcast = specialSystemCarrierAppIntents[i];
+ break;
+ }
+ }
+
+ if (TextUtils.isEmpty(operatorCodesToMatch)) {
+ // There is an error in the configuration
+ Slog.e(TAG, "No valid operator codes found in the configuration for candidate "
+ + ai.packageName);
+ // Do not try to match against available operator codes
+ shouldBeMatched = false;
+ } else if (DEBUG) {
+ Slog.d(TAG, "Special carrier app candidate (" + ai.packageName
+ + ") wants to match against " + operatorCodesToMatch);
+ }
+
+ SpecialCarrierAppInfo appInfo = new SpecialCarrierAppInfo(ai.packageName,
+ operatorCodesToMatch == null ? "" : operatorCodesToMatch,
+ actionIntentToBroadcast);
+
+ if (shouldBeMatched) {
+ for (int uiccSlot = 0; uiccSlot < simOperatorCodes.length; uiccSlot++) {
+ if (appInfo.matchesAgainstOperator(simOperatorCodes[uiccSlot])) {
+ appInfo.mUiccSlotId = uiccSlot;
+ appInfo.mCarrierName = TelephonyManager.getDefault().getSimOperatorNameForPhone(uiccSlot);
+ break;
+ }
+ }
+ }
+
+ if (appInfo.hasMatched()
+ && (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || ai.enabledSetting ==
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+ if (DEBUG) Slog.d(TAG, "Special carrier app candidate (" + ai.packageName
+ + ") found a match with UICC " + appInfo.mUiccSlotId);
+
+ boolean isDeviceProvisioned =
+ Settings.Global.getInt(context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED) == 1;
+
+ if (new UserHandle(userId).isOwner()) {
+ if (DEBUG) Slog.d(TAG, "Launching the Telephony service to handle the special carrier app candidate ("
+ + ai.packageName + ") for owner user " + userId);
+ /*
+ * Defer to the Telephony service to handle the app match for the owner.
+ */
+ context.startService(new Intent()
+ .setClassName(context, SPECIAL_CARRIER_APP_INTENT_SERVICE_CLASS)
+ .setAction(SPECIAL_CARRIER_APP_ACTION_HANDLE_MATCH)
+ .putExtra(SPECIAL_CARRIER_APP_EXTRA_APP_INFO, appInfo)
+ .putExtra(SPECIAL_CARRIER_APP_EXTRA_DEVICE_PROVISIONED, isDeviceProvisioned));
+ } else {
+ /*
+ * FIXME: Non-owner users will not have a notification.
+ * The app is left "disabled until used" because there is only one user
+ * created by the first boot - and that is the owner.
+ */
+ Slog.i(TAG, "Update state(" + ai.packageName
+ + "): DISABLED_UNTIL_USED for non-owner user " + userId);
+ packageManager.setApplicationEnabledSetting(ai.packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
+ userId, context.getOpPackageName());
+ }
+ } else if (!appInfo.hasMatched()
+ && ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ Slog.i(TAG, "Update state(" + ai.packageName
+ + "): DISABLED_UNTIL_USED for user " + userId);
+ packageManager.setApplicationEnabledSetting(ai.packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
+ userId, context.getOpPackageName());
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not reach PackageManager", e);
+ } catch (Settings.SettingNotFoundException e) {
+ Slog.e(TAG, "Could not read the device provision", e);
+ }
+ }
+
+ /**
* Returns the list of "default" carrier apps.
*
* This is the subset of apps returned by
diff --git a/telephony/java/com/android/internal/telephony/ISmsSecurityAgent.aidl b/telephony/java/com/android/internal/telephony/ISmsSecurityAgent.aidl
new file mode 100644
index 0000000..3b52529
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ISmsSecurityAgent.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.SmsAuthorizationRequest;
+
+/**
+ * ISmsSecurityAgent enhances the security of outgoing SMS messages by allowing trusted system
+ * components to inspect and authorize or reject outgoing SMS messages.
+ *
+ * @hide
+ **/
+interface ISmsSecurityAgent {
+ /**
+ * Called when a SMS message is queued for dispatch allowing a registered
+ * agent to decide on whether to accept/reject the request to send an SMS message.
+ * <b>Unless the agent rejects the request within the OEM specific timeout, the SMS
+ * will be sent.</b>
+ * @param request the object containing information regarding the message and
+ * through which the agent can accept/reject the request.
+ */
+ void onAuthorize(in SmsAuthorizationRequest request);
+
+}
diff --git a/telephony/java/com/android/internal/telephony/ISmsSecurityService.aidl b/telephony/java/com/android/internal/telephony/ISmsSecurityService.aidl
new file mode 100644
index 0000000..e479f0c
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ISmsSecurityService.aidl
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.ISmsSecurityAgent;
+import com.android.internal.telephony.SmsAuthorizationRequest;
+
+/**
+ * ISmsSecurityService exposes a service that monitors the dispatch of outgoing SMS messages
+ * and notifies a registered ISmsSecurityAgent in order to authorize or reject the dispatch
+ * of each outgoing SMS message.
+ *
+ * @hide
+ */
+interface ISmsSecurityService {
+ /**
+ * Registers an agent in order to receive requests for outgoing SMS messages on which
+ * it can accept or reject the request for the dispatch of each SMS message.
+ * <b>Only one agent can be registered at one time.</b>
+ * @param agent the agent to be registered.
+ * @return true if the registration succeeds, false otherwise.
+ */
+ boolean register(in ISmsSecurityAgent agent);
+
+ /**
+ * Unregisters the previously registered agent and causes the security
+ * service to no longer rely on the agent for a decision regarding
+ * successive SMS messages being dispatched allowing all successive messages to be dispatched.
+ *
+ * @param agent the agent to be unregistered.
+ * @return true if the unregistration succeeds, false otherwise.
+ */
+ boolean unregister(in ISmsSecurityAgent agent);
+
+ /**
+ * Allows the registered ISmsSecurityAgent implementation to asynchronously send a response
+ * on whether it will accept/reject the dispatch of the SMS message.
+ * <b>If the agent responds after the OEM defined timeout it may not be able to
+ * interfere on whether the SMS was sent or not.</b>
+ * @param request the request related to an outgoing SMS message to accept/reject.
+ * @param accepted true to accept, false to reject.
+ * return true if the response took effect, false if a response has already been sent for this
+ * request or an OEM specific timeout already happened.
+ */
+ boolean sendResponse(in SmsAuthorizationRequest request, boolean authorized);
+}
diff --git a/telephony/java/com/android/internal/telephony/SmsAuthorizationRequest.aidl b/telephony/java/com/android/internal/telephony/SmsAuthorizationRequest.aidl
new file mode 100644
index 0000000..a2f7020
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/SmsAuthorizationRequest.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.internal.telephony;
+
+/** @hide */
+parcelable SmsAuthorizationRequest;
diff --git a/telephony/java/com/android/internal/telephony/SmsAuthorizationRequest.java b/telephony/java/com/android/internal/telephony/SmsAuthorizationRequest.java
new file mode 100644
index 0000000..bc64fa8
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/SmsAuthorizationRequest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+/**
+ * This class represents a request from the {@link ISmsSecurityService} to trusted parties
+ * in order to allow third party components to participate in the decision process to accept
+ * or reject a request to send an SMS message.
+ *
+ * @hide
+ */
+public class SmsAuthorizationRequest implements Parcelable {
+
+ private final ISmsSecurityService service;
+
+ private final IBinder token;
+
+ public final String packageName;
+
+ public final String destinationAddress;
+
+ public final String message;
+
+ public SmsAuthorizationRequest(final Parcel source) {
+ this.service = ISmsSecurityService.Stub.asInterface(source.readStrongBinder());
+ this.token = source.readStrongBinder();
+ this.packageName = source.readString();
+ this.destinationAddress = source.readString();
+ this.message = source.readString();
+ }
+
+ public SmsAuthorizationRequest(final ISmsSecurityService service,
+ final IBinder binderToken,
+ final String packageName,
+ final String destinationAddress,
+ final String message) {
+ this.service = service;
+ this.token = binderToken;
+ this.packageName = packageName;
+ this.destinationAddress = destinationAddress;
+ this.message = message;
+ }
+
+ @Override
+ public void writeToParcel(final Parcel dest, final int flags) {
+ dest.writeStrongBinder(service.asBinder());
+ dest.writeStrongBinder(token);
+ dest.writeString(packageName);
+ dest.writeString(destinationAddress);
+ dest.writeString(message);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static Parcelable.Creator<SmsAuthorizationRequest> CREATOR =
+ new Creator<SmsAuthorizationRequest>() {
+ @Override
+ public SmsAuthorizationRequest[] newArray(final int size) {
+ return new SmsAuthorizationRequest[size];
+ }
+
+ @Override
+ public SmsAuthorizationRequest createFromParcel(final Parcel source) {
+ return new SmsAuthorizationRequest(source);
+ }
+ };
+
+ public void accept() throws RemoteException{
+ service.sendResponse(this, true);
+ }
+
+ public void reject() throws RemoteException {
+ service.sendResponse(this, false);
+ }
+
+ public IBinder getToken() {
+ return token;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[%s] (%s) # %s",
+ this.packageName,
+ this.destinationAddress,
+ this.message);
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/SpecialCarrierAppInfo.java b/telephony/java/com/android/internal/telephony/SpecialCarrierAppInfo.java
new file mode 100644
index 0000000..64c45b6
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/SpecialCarrierAppInfo.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017-2020 Fairphone B.V.
+ *
+ * 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.internal.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.Serializable;
+
+public class SpecialCarrierAppInfo implements Serializable {
+ /**
+ * The app package name.
+ */
+ @NonNull
+ public String mPackageName;
+ /**
+ * The operator codes to match, separated by comma.
+ */
+ @NonNull
+ public String mOperatorCodesToMatch;
+ /**
+ * The optional action intent to broadcast after enabling the app.
+ */
+ @Nullable
+ public String mActionIntentToBroadcast;
+ /**
+ * The matched UICC slot id, or <code>-1</code> if not matched.
+ */
+ public int mUiccSlotId;
+ /**
+ * The carrier name as described by the matched UICC, or <code>null</code>.
+ */
+ @Nullable
+ public String mCarrierName;
+
+ public SpecialCarrierAppInfo(@NonNull String packageName, @NonNull String operatorCodesToMatch,
+ String actionIntentToBroadcast) {
+ this(packageName, operatorCodesToMatch, actionIntentToBroadcast, -1, null);
+ }
+
+ private SpecialCarrierAppInfo(@NonNull String packageName, @NonNull String operatorCodesToMatch,
+ String actionIntentToBroadcast, int uiccSlotId, String carrierName) {
+ mPackageName = packageName;
+ mOperatorCodesToMatch = operatorCodesToMatch;
+ mActionIntentToBroadcast = actionIntentToBroadcast;
+ mUiccSlotId = uiccSlotId;
+ mCarrierName = carrierName;
+ }
+
+ /**
+ * @return All operator codes to match, or an empty array if none.
+ */
+ @NonNull
+ public String[] getAllOperatorCodesToMatch() {
+ return mOperatorCodesToMatch.split(",");
+ }
+
+ public boolean matchesAgainstOperator(@Nullable String operatorCode) {
+ for (String operatorCodeToMatch : getAllOperatorCodesToMatch()) {
+ if (operatorCodeToMatch.equals(operatorCode)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean hasMatched() {
+ return mUiccSlotId != -1;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index c095438..4790b75 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -105,7 +105,7 @@
/**
* PLMN (MCC/MNC) is encoded as per 24.008 10.5.1.3
* Returns a concatenated string of MCC+MNC, stripping
- * all invalid character 'f'
+ * all invalid character 'F'
*/
public static String bcdPlmnToString(byte[] data, int offset) {
if (offset + 3 > data.length) {
@@ -117,9 +117,9 @@
trans[2] = (byte) ((data[2 + offset] & 0xF0) | ((data[1 + offset] >> 4) & 0xF));
String ret = bytesToHexString(trans);
- // For a valid plmn we trim all character 'f'
- if (ret.contains("f")) {
- ret = ret.replaceAll("f", "");
+ // For a valid plmn we trim all character 'F'
+ if (ret.contains("F")) {
+ ret = ret.replaceAll("F", "");
}
return ret;
}