Merge "Improve theme application transition." into qt-dev
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 74ceeb9..388161d 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -56,7 +56,8 @@
* returned by {@link BluetoothAdapter#getBondedDevices()
* BluetoothAdapter.getBondedDevices()}. You can then open a
* {@link BluetoothSocket} for communication with the remote device, using
- * {@link #createRfcommSocketToServiceRecord(UUID)}.
+ * {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using
+ * {@link #createL2capChannel(int)} over Bluetooth LE.
*
* <p class="note"><strong>Note:</strong>
* Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 4e88625..c06b837 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -35,21 +35,28 @@
* On the client side, use a single {@link BluetoothSocket} to both initiate
* an outgoing connection and to manage the connection.
*
- * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
- * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
- * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
+ * <p>For Bluetooth BR/EDR, the most common type of socket is RFCOMM, which is the type supported by
+ * the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth BR/EDR. It
+ * is also known as the Serial Port Profile (SPP). To create a listening
+ * {@link BluetoothServerSocket} that's ready for incoming Bluetooth BR/EDR connections, use {@link
+ * BluetoothAdapter#listenUsingRfcommWithServiceRecord
+ * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}.
*
- * <p>To create a listening {@link BluetoothServerSocket} that's ready for
- * incoming connections, use
- * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord
- * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call
- * {@link #accept()} to listen for incoming connection requests. This call
- * will block until a connection is established, at which point, it will return
- * a {@link BluetoothSocket} to manage the connection. Once the {@link
- * BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on
- * the {@link BluetoothServerSocket} when it's no longer needed for accepting
- * connections. Closing the {@link BluetoothServerSocket} will <em>not</em>
- * close the returned {@link BluetoothSocket}.
+ * <p>For Bluetooth LE, the socket uses LE Connection-oriented Channel (CoC). LE CoC is a
+ * connection-oriented, streaming transport over Bluetooth LE and has a credit-based flow control.
+ * Correspondingly, use {@link BluetoothAdapter#listenUsingL2capChannel
+ * BluetoothAdapter.listenUsingL2capChannel()} to create a listening {@link BluetoothServerSocket}
+ * that's ready for incoming Bluetooth LE CoC connections. For LE CoC, you can use {@link #getPsm()}
+ * to get the protocol/service multiplexer (PSM) value that the peer needs to use to connect to your
+ * socket.
+ *
+ * <p> After the listening {@link BluetoothServerSocket} is created, call {@link #accept()} to
+ * listen for incoming connection requests. This call will block until a connection is established,
+ * at which point, it will return a {@link BluetoothSocket} to manage the connection. Once the
+ * {@link BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on the {@link
+ * BluetoothServerSocket} when it's no longer needed for accepting
+ * connections. Closing the {@link BluetoothServerSocket} will <em>not</em> close the returned
+ * {@link BluetoothSocket}.
*
* <p>{@link BluetoothServerSocket} is thread
* safe. In particular, {@link #close} will always immediately abort ongoing
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 24f42d4..4da0d2d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7983,6 +7983,16 @@
"lock_screen_show_notifications";
/**
+ * Indicates whether the lock screen should display silent notifications.
+ * <p>
+ * Type: int (0 for false, 1 for true)
+ *
+ * @hide
+ */
+ public static final String LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS =
+ "lock_screen_show_silent_notifications";
+
+ /**
* List of TV inputs that are currently hidden. This is a string
* containing the IDs of all hidden TV inputs. Each ID is encoded by
* {@link android.net.Uri#encode(String)} and separated by ':'.
@@ -8838,6 +8848,7 @@
LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
LOCK_SCREEN_CUSTOM_CLOCK_FACE,
LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
ZEN_DURATION,
SHOW_ZEN_UPGRADE_NOTIFICATION,
SHOW_ZEN_SETTINGS_SUGGESTION,
@@ -9013,6 +9024,7 @@
VALIDATORS.put(IN_CALL_NOTIFICATION_ENABLED, IN_CALL_NOTIFICATION_ENABLED_VALIDATOR);
VALIDATORS.put(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, BOOLEAN_VALIDATOR);
VALIDATORS.put(LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, BOOLEAN_VALIDATOR);
VALIDATORS.put(ZEN_DURATION, ZEN_DURATION_VALIDATOR);
VALIDATORS.put(SHOW_ZEN_UPGRADE_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(SHOW_ZEN_SETTINGS_SUGGESTION, BOOLEAN_VALIDATOR);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index a296d64..3a25e67 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -26,6 +26,7 @@
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
+#include <audiomanager/AudioManager.h>
#include <media/AudioSystem.h>
#include <media/AudioPolicy.h>
#include <media/MicrophoneInfo.h>
@@ -353,7 +354,15 @@
static jint
android_media_AudioSystem_newAudioPlayerId(JNIEnv *env, jobject thiz)
{
- return AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_PLAYER);
+ int id = AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_CLIENT);
+ return id != AUDIO_UNIQUE_ID_ALLOCATE ? id : PLAYER_PIID_INVALID;
+}
+
+static jint
+android_media_AudioSystem_newAudioRecorderId(JNIEnv *env, jobject thiz)
+{
+ int id = AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_CLIENT);
+ return id != AUDIO_UNIQUE_ID_ALLOCATE ? id : RECORD_RIID_INVALID;
}
static jint
@@ -470,9 +479,10 @@
env->CallStaticVoidMethod(clazz,
gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
- event, (jint) clientInfo->uid, clientInfo->session,
- clientInfo->source, clientInfo->port_id, clientInfo->silenced,
- recParamArray, jClientEffects, jEffects, source);
+ event, (jint) clientInfo->riid, (jint) clientInfo->uid,
+ clientInfo->session, clientInfo->source, clientInfo->port_id,
+ clientInfo->silenced, recParamArray, jClientEffects, jEffects,
+ source);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(recParamArray);
env->DeleteLocalRef(jClientEffects);
@@ -2246,6 +2256,7 @@
{"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive},
{"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId},
{"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId},
+ {"newAudioRecorderId", "()I", (void *)android_media_AudioSystem_newAudioRecorderId},
{"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;I)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
{"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState},
{"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I", (void *)android_media_AudioSystem_handleDeviceConfigChange},
@@ -2440,7 +2451,7 @@
"dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
- "recordingCallbackFromNative", "(IIIIIZ[I[Landroid/media/audiofx/AudioEffect$Descriptor;[Landroid/media/audiofx/AudioEffect$Descriptor;I)V");
+ "recordingCallbackFromNative", "(IIIIIIZ[I[Landroid/media/audiofx/AudioEffect$Descriptor;[Landroid/media/audiofx/AudioEffect$Descriptor;I)V");
jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index f8e43437..bcc57d2 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3766,9 +3766,24 @@
* with frameworks/av/include/media/AudioPolicy.h
*/
/** @hide */
- public final static int RECORD_CONFIG_EVENT_START = 1;
+ public static final int RECORD_CONFIG_EVENT_NONE = -1;
/** @hide */
- public final static int RECORD_CONFIG_EVENT_STOP = 0;
+ public static final int RECORD_CONFIG_EVENT_START = 0;
+ /** @hide */
+ public static final int RECORD_CONFIG_EVENT_STOP = 1;
+ /** @hide */
+ public static final int RECORD_CONFIG_EVENT_UPDATE = 2;
+ /** @hide */
+ public static final int RECORD_CONFIG_EVENT_DEATH = 3;
+ /**
+ * keep in sync with frameworks/native/include/audiomanager/AudioManager.h
+ */
+ /** @hide */
+ public static final int RECORD_RIID_INVALID = -1;
+ /** @hide */
+ public static final int RECORDER_STATE_STARTED = 0;
+ /** @hide */
+ public static final int RECORDER_STATE_STOPPED = 1;
/**
* All operations on this list are sync'd on mRecordCallbackLock.
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 82bcbc1..74e6618 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -157,7 +157,7 @@
return new AudioRecordingConfiguration( /*anonymized uid*/ -1,
in.mClientSessionId, in.mClientSource, in.mClientFormat,
in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/,
- in.mClientPortId, in.mClientSilenced, in.mDeviceSource, in.mClientEffects,
+ /*anonymized portId*/ -1, in.mClientSilenced, in.mDeviceSource, in.mClientEffects,
in.mDeviceEffects);
}
@@ -270,11 +270,12 @@
}
/**
+ * @hide
* Returns the system unique ID assigned for the AudioRecord object corresponding to this
* AudioRecordingConfiguration client.
* @return the port ID.
*/
- int getClientPortId() {
+ public int getClientPortId() {
return mClientPortId;
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index d105fa3..987db8b 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -220,6 +220,11 @@
*/
public static native int newAudioPlayerId();
+ /**
+ * Returns a new unused audio recorder ID
+ */
+ public static native int newAudioRecorderId();
+
/*
* Sets a group generic audio configuration parameters. The use of these parameters
@@ -347,6 +352,7 @@
/**
* Callback for recording activity notifications events
* @param event
+ * @param riid recording identifier
* @param uid uid of the client app performing the recording
* @param session
* @param source
@@ -361,7 +367,7 @@
* 6: patch handle
* @param packName package name of the client app performing the recording. NOT SUPPORTED
*/
- void onRecordingConfigurationChanged(int event, int uid, int session, int source,
+ void onRecordingConfigurationChanged(int event, int riid, int uid, int session, int source,
int portId, boolean silenced, int[] recordingFormat,
AudioEffect.Descriptor[] clienteffects, AudioEffect.Descriptor[] effects,
int activeSource, String packName);
@@ -379,16 +385,23 @@
/**
* Callback from native for recording configuration updates.
* @param event
+ * @param riid
+ * @param uid
* @param session
* @param source
+ * @param portId
+ * @param silenced
* @param recordingFormat see
- * {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int,\
- boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
+ * {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int, \
+ int, boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
* for the description of the record format.
+ * @param cleintEffects
+ * @param effects
+ * @param activeSource
*/
@UnsupportedAppUsage
- private static void recordingCallbackFromNative(int event, int uid, int session, int source,
- int portId, boolean silenced, int[] recordingFormat,
+ private static void recordingCallbackFromNative(int event, int riid, int uid, int session,
+ int source, int portId, boolean silenced, int[] recordingFormat,
AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects,
int activeSource) {
AudioRecordingCallback cb = null;
@@ -401,7 +414,7 @@
if (cb != null) {
// TODO receive package name from native
- cb.onRecordingConfigurationChanged(event, uid, session, source, portId, silenced,
+ cb.onRecordingConfigurationChanged(event, riid, uid, session, source, portId, silenced,
recordingFormat, clientEffects, effects, activeSource, "");
}
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 85de00a..81ddcdb4 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -60,6 +60,10 @@
oneway void releasePlayer(in int piid);
+ int trackRecorder(in IBinder recorder);
+
+ oneway void recorderEvent(in int riid, in int event);
+
// Java-only methods below.
oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
index fccb719..93a34c0 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.TypedArray;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -73,7 +74,12 @@
mIcon.setImageDrawable(barViewInfo.getIcon());
mBarTitle.setText(barViewInfo.getTitle());
mBarSummary.setText(barViewInfo.getSummary());
- mIcon.setContentDescription(barViewInfo.getContentDescription());
+
+ final CharSequence barViewInfoContent = barViewInfo.getContentDescription();
+ if (!TextUtils.isEmpty(barViewInfoContent)
+ && !TextUtils.equals((barViewInfo.getTitle()), barViewInfoContent)) {
+ mIcon.setContentDescription(barViewInfo.getContentDescription());
+ }
}
@VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
index ae8e1e2..786139f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
@@ -45,7 +45,7 @@
Settings.Global.getLong(
resolver, Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME, -1))
return if (Duration.between(lastUpdateTime,
- Instant.now()).compareTo(Duration.ofMinutes(2)) > 0) {
+ Instant.now()).compareTo(Duration.ofMinutes(1)) > 0) {
null
} else Estimate(
Settings.Global.getLong(resolver,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 9dd5bb4..bd7b3d5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -105,7 +105,8 @@
}
t.setEarlyWakeup();
t.apply();
- mApplyHandler.sendEmptyMessage(toApplySeqNo);
+ Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
+ .sendToTarget();
}
});
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 135b351..07c2f10 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -289,12 +289,19 @@
public void onPluginConnected(ClockPlugin plugin, Context pluginContext) {
addClockPlugin(plugin);
reload();
+ if (plugin == mCurrentClock) {
+ ClockManager.this.reload();
+ }
}
@Override
public void onPluginDisconnected(ClockPlugin plugin) {
+ boolean isCurrentClock = plugin == mCurrentClock;
removeClockPlugin(plugin);
reload();
+ if (isCurrentClock) {
+ ClockManager.this.reload();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 6c1d1f9..6832ee3 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -36,6 +36,7 @@
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -319,6 +320,9 @@
mUser = ActivityManager.getCurrentUser();
getContext().getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
+ getContext().getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
+ false, mSettingObserver);
updateShowPercent();
subscribeForTunerUpdates();
mUserTracker.startTracking();
@@ -493,6 +497,11 @@
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
updateShowPercent();
+ if (TextUtils.equals(uri.getLastPathSegment(),
+ Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME)) {
+ // update the text for sure if the estimate in the cache was updated
+ updatePercentText();
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index ed59f79..9490b14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -306,7 +306,8 @@
return false;
}
boolean exceedsPriorityThreshold;
- if (NotificationUtils.useNewInterruptionModel(mContext)) {
+ if (NotificationUtils.useNewInterruptionModel(mContext)
+ && hideSilentNotificationsOnLockscreen()) {
exceedsPriorityThreshold = getEntryManager().getNotificationData().isHighPriority(sbn);
} else {
exceedsPriorityThreshold =
@@ -315,6 +316,11 @@
return mShowLockscreenNotifications && exceedsPriorityThreshold;
}
+ private boolean hideSilentNotificationsOnLockscreen() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0) == 0;
+ }
+
private void setShowLockscreenNotifications(boolean show) {
mShowLockscreenNotifications = show;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index fde1455..9c7a1e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -73,7 +73,6 @@
private boolean mTestmode = false;
private boolean mHasReceivedBattery = false;
private Estimate mEstimate;
- private long mLastEstimateTimestamp = -1;
private boolean mFetchingEstimate = false;
@Inject
@@ -203,13 +202,6 @@
@Override
public void getEstimatedTimeRemainingString(EstimateFetchCompletion completion) {
- if (mEstimate != null
- && mLastEstimateTimestamp > System.currentTimeMillis() - UPDATE_GRANULARITY_MSEC) {
- String percentage = generateTimeRemainingString();
- completion.onBatteryRemainingEstimateRetrieved(percentage);
- return;
- }
-
// Need to fetch or refresh the estimate, but it may involve binder calls so offload the
// work
synchronized (mFetchCallbacks) {
@@ -238,8 +230,10 @@
mFetchingEstimate = true;
Dependency.get(Dependency.BG_HANDLER).post(() -> {
// Only fetch the estimate if they are enabled
- mEstimate = mEstimates.isHybridNotificationEnabled() ? mEstimates.getEstimate() : null;
- mLastEstimateTimestamp = System.currentTimeMillis();
+ mEstimate = null;
+ if (mEstimates.isHybridNotificationEnabled()) {
+ updateEstimate();
+ }
mFetchingEstimate = false;
Dependency.get(Dependency.MAIN_HANDLER).post(this::notifyEstimateFetchCallbacks);
});
@@ -258,8 +252,15 @@
}
private void updateEstimate() {
- mEstimate = mEstimates.getEstimate();
- mLastEstimateTimestamp = System.currentTimeMillis();
+ // if the estimate has been cached we can just use that, otherwise get a new one and
+ // throw it in the cache.
+ mEstimate = Estimate.getCachedEstimateIfAvailable(mContext);
+ if (mEstimate == null) {
+ mEstimate = mEstimates.getEstimate();
+ if (mEstimate != null) {
+ Estimate.storeCachedEstimate(mContext, mEstimate);
+ }
+ }
}
private void updatePowerSave() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 490b2ea..49a263a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -17,10 +17,13 @@
package com.android.systemui.statusbar;
import static android.content.Intent.ACTION_USER_SWITCHED;
+import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -35,6 +38,7 @@
import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -152,7 +156,34 @@
assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUserId));
}
- private class TestNotificationLockscreenUserManager extends NotificationLockscreenUserManagerImpl {
+ @Test
+ public void testShowSilentNotifications_settingSaysShow() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
+ when(mNotificationData.isHighPriority(any())).thenReturn(false);
+
+ assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(mock(StatusBarNotification.class)));
+ }
+
+ @Test
+ public void testShowSilentNotifications_settingSaysHide() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+ when(mNotificationData.isHighPriority(any())).thenReturn(false);
+
+ assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(mock(StatusBarNotification.class)));
+ }
+
+ private class TestNotificationLockscreenUserManager
+ extends NotificationLockscreenUserManagerImpl {
public TestNotificationLockscreenUserManager(Context context) {
super(context);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d3af8f07..e38defd 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -409,16 +409,16 @@
private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
public void onError(int error) {
switch (error) {
- case AudioSystem.AUDIO_STATUS_SERVER_DIED:
- mRecordMonitor.clear();
+ case AudioSystem.AUDIO_STATUS_SERVER_DIED:
+ mRecordMonitor.onAudioServerDied();
- sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED,
- SENDMSG_NOOP, 0, 0, null, 0);
- sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
- SENDMSG_QUEUE, 0, 0, null, 0);
- break;
- default:
- break;
+ sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED,
+ SENDMSG_NOOP, 0, 0, null, 0);
+ sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
+ SENDMSG_QUEUE, 0, 0, null, 0);
+ break;
+ default:
+ break;
}
}
};
@@ -6943,6 +6943,23 @@
return mRecordMonitor.getActiveRecordingConfigurations(isPrivileged);
}
+ //======================
+ // Audio recording state notification from clients
+ //======================
+ /**
+ * Track a recorder provided by the client
+ */
+ public int trackRecorder(IBinder recorder) {
+ return mRecordMonitor.trackRecorder(recorder);
+ }
+
+ /**
+ * Receive an event from the client about a tracked recorder
+ */
+ public void recorderEvent(int riid, int event) {
+ mRecordMonitor.recorderEvent(riid, event);
+ }
+
public void disableRingtoneSync(final int userId) {
final int callingUserId = UserHandle.getCallingUserId();
if (callingUserId != userId) {
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 87b272b..69d1ea7 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -33,7 +33,6 @@
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -50,50 +49,148 @@
// playback configurations that do not contain uid/package name information.
private boolean mHasPublicClients = false;
- private HashMap<Integer, AudioRecordingConfiguration> mRecordConfigs =
- new HashMap<Integer, AudioRecordingConfiguration>();
+ static final class RecordingState {
+ private final int mRiid;
+ private final RecorderDeathHandler mDeathHandler;
+ private boolean mIsActive;
+ private AudioRecordingConfiguration mConfig;
+
+ RecordingState(int riid, RecorderDeathHandler handler) {
+ mRiid = riid;
+ mDeathHandler = handler;
+ }
+
+ RecordingState(AudioRecordingConfiguration config) {
+ mRiid = AudioManager.RECORD_RIID_INVALID;
+ mDeathHandler = null;
+ mConfig = config;
+ }
+
+ int getRiid() {
+ return mRiid;
+ }
+
+ int getPortId() {
+ return mConfig != null ? mConfig.getClientPortId() : -1;
+ }
+
+ AudioRecordingConfiguration getConfig() {
+ return mConfig;
+ }
+
+ boolean hasDeathHandler() {
+ return mDeathHandler != null;
+ }
+
+ boolean isActiveConfiguration() {
+ return mIsActive && mConfig != null;
+ }
+
+ // returns true if status of an active recording has changed
+ boolean setActive(boolean active) {
+ if (mIsActive == active) return false;
+ mIsActive = active;
+ return mConfig != null;
+ }
+
+ // returns true if an active recording has been updated
+ boolean setConfig(AudioRecordingConfiguration config) {
+ if (config.equals(mConfig)) return false;
+ mConfig = config;
+ return mIsActive;
+ }
+
+ void dump(PrintWriter pw) {
+ pw.println("riid " + mRiid + "; active? " + mIsActive);
+ if (mConfig != null) {
+ mConfig.dump(pw);
+ } else {
+ pw.println(" no config");
+ }
+ }
+ }
+ private List<RecordingState> mRecordStates = new ArrayList<RecordingState>();
private final PackageManager mPackMan;
RecordingActivityMonitor(Context ctxt) {
RecMonitorClient.sMonitor = this;
+ RecorderDeathHandler.sMonitor = this;
mPackMan = ctxt.getPackageManager();
}
/**
* Implementation of android.media.AudioSystem.AudioRecordingCallback
*/
- public void onRecordingConfigurationChanged(int event, int uid, int session, int source,
- int portId, boolean silenced, int[] recordingInfo,
+ public void onRecordingConfigurationChanged(int event, int riid, int uid, int session,
+ int source, int portId, boolean silenced,
+ int[] recordingInfo,
AudioEffect.Descriptor[] clientEffects,
AudioEffect.Descriptor[] effects,
int activeSource, String packName) {
+ final AudioRecordingConfiguration config = createRecordingConfiguration(
+ uid, session, source, recordingInfo,
+ portId, silenced, activeSource, clientEffects, effects);
if (MediaRecorder.isSystemOnlyAudioSource(source)) {
- // still want to log event, it just won't appear in recording configurations
- sEventLogger.log(new RecordingEvent(event, uid, session, source, packName)
- .printLog(TAG));
+ // still want to log event, it just won't appear in recording configurations;
+ sEventLogger.log(new RecordingEvent(event, riid, config).printLog(TAG));
return;
}
- String clientEffectName = clientEffects.length == 0 ? "None" : clientEffects[0].name;
- String effectName = effects.length == 0 ? "None" : effects[0].name;
-
- final List<AudioRecordingConfiguration> configsSystem =
- updateSnapshot(event, uid, session, source, recordingInfo,
- portId, silenced, activeSource, clientEffects, effects);
- if (configsSystem != null){
- dispatchCallbacks(configsSystem);
- }
+ dispatchCallbacks(updateSnapshot(event, riid, config));
}
+
+ /**
+ * Track a recorder provided by the client
+ */
+ public int trackRecorder(IBinder recorder) {
+ if (recorder == null) {
+ Log.e(TAG, "trackRecorder called with null token");
+ return AudioManager.RECORD_RIID_INVALID;
+ }
+ final int newRiid = AudioSystem.newAudioRecorderId();
+ RecorderDeathHandler handler = new RecorderDeathHandler(newRiid, recorder);
+ if (!handler.init()) {
+ // probably means that the AudioRecord has already died
+ return AudioManager.RECORD_RIID_INVALID;
+ }
+ synchronized (mRecordStates) {
+ mRecordStates.add(new RecordingState(newRiid, handler));
+ }
+ // a newly added record is inactive, no change in active configs is possible.
+ return newRiid;
+ }
+
+ /**
+ * Receive an event from the client about a tracked recorder
+ */
+ public void recorderEvent(int riid, int event) {
+ int configEvent = event == AudioManager.RECORDER_STATE_STARTED
+ ? AudioManager.RECORD_CONFIG_EVENT_START :
+ event == AudioManager.RECORDER_STATE_STOPPED
+ ? AudioManager.RECORD_CONFIG_EVENT_STOP : AudioManager.RECORD_CONFIG_EVENT_NONE;
+ if (riid == AudioManager.RECORD_RIID_INVALID
+ || configEvent == AudioManager.RECORD_CONFIG_EVENT_NONE) {
+ sEventLogger.log(new RecordingEvent(event, riid, null).printLog(TAG));
+ return;
+ }
+ dispatchCallbacks(updateSnapshot(configEvent, riid, null));
+ }
+
+ void unregisterRecorder(int riid) {
+ dispatchCallbacks(updateSnapshot(AudioManager.RECORD_CONFIG_EVENT_DEATH, riid, null));
+ }
+
private void dispatchCallbacks(List<AudioRecordingConfiguration> configs) {
+ if (configs == null) { // null means "no changes"
+ return;
+ }
synchronized (mClients) {
// list of recording configurations for "public consumption". It is only computed if
// there are non-system recording activity listeners.
final List<AudioRecordingConfiguration> configsPublic = mHasPublicClients
? anonymizeForPublicConsumption(configs) :
new ArrayList<AudioRecordingConfiguration>();
- final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
- while (clientIterator.hasNext()) {
- final RecMonitorClient rmc = clientIterator.next();
+ for (RecMonitorClient rmc : mClients) {
try {
if (rmc.mIsPrivileged) {
rmc.mDispatcherCb.dispatchRecordingConfigChange(configs);
@@ -108,12 +205,12 @@
}
protected void dump(PrintWriter pw) {
- // players
+ // recorders
pw.println("\nRecordActivityMonitor dump time: "
+ DateFormat.getTimeInstance().format(new Date()));
- synchronized(mRecordConfigs) {
- for (AudioRecordingConfiguration conf : mRecordConfigs.values()) {
- conf.dump(pw);
+ synchronized (mRecordStates) {
+ for (RecordingState state : mRecordStates) {
+ state.dump(pw);
}
}
pw.println("\n");
@@ -121,7 +218,7 @@
sEventLogger.dump(pw);
}
- private ArrayList<AudioRecordingConfiguration> anonymizeForPublicConsumption(
+ private static ArrayList<AudioRecordingConfiguration> anonymizeForPublicConsumption(
List<AudioRecordingConfiguration> sysConfigs) {
ArrayList<AudioRecordingConfiguration> publicConfigs =
new ArrayList<AudioRecordingConfiguration>();
@@ -136,11 +233,30 @@
AudioSystem.setRecordingCallback(this);
}
- void clear() {
- synchronized (mRecordConfigs) {
- mRecordConfigs.clear();
+ void onAudioServerDied() {
+ // Remove all RecordingState entries that do not have a death handler (that means
+ // they are tracked by the Audio Server). If there were active entries among removed,
+ // dispatch active configuration changes.
+ List<AudioRecordingConfiguration> configs = null;
+ synchronized (mRecordStates) {
+ boolean configChanged = false;
+ for (Iterator<RecordingState> it = mRecordStates.iterator(); it.hasNext(); ) {
+ RecordingState state = it.next();
+ if (!state.hasDeathHandler()) {
+ if (state.isActiveConfiguration()) {
+ configChanged = true;
+ sEventLogger.log(new RecordingEvent(
+ AudioManager.RECORD_CONFIG_EVENT_DEATH,
+ state.getRiid(), state.getConfig()));
+ }
+ it.remove();
+ }
+ }
+ if (configChanged) {
+ configs = getActiveRecordingConfigurations(true /*isPrivileged*/);
+ }
}
- dispatchCallbacks(new ArrayList<AudioRecordingConfiguration>());
+ dispatchCallbacks(configs);
}
void registerRecordingCallback(IRecordingConfigDispatcher rcdb, boolean isPrivileged) {
@@ -181,21 +297,25 @@
}
List<AudioRecordingConfiguration> getActiveRecordingConfigurations(boolean isPrivileged) {
- synchronized(mRecordConfigs) {
- if (isPrivileged) {
- return new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values());
- } else {
- final List<AudioRecordingConfiguration> configsPublic =
- anonymizeForPublicConsumption(
- new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values()));
- return configsPublic;
+ List<AudioRecordingConfiguration> configs = new ArrayList<AudioRecordingConfiguration>();
+ synchronized (mRecordStates) {
+ for (RecordingState state : mRecordStates) {
+ if (state.isActiveConfiguration()) {
+ configs.add(state.getConfig());
+ }
}
}
+ // AudioRecordingConfiguration objects never get updated. If config changes,
+ // the reference to the config is set in RecordingState.
+ if (!isPrivileged) {
+ configs = anonymizeForPublicConsumption(configs);
+ }
+ return configs;
}
/**
- * Update the internal "view" of the active recording sessions
- * @param event
+ * Create a recording configuration from the provided parameters
+ * @param uid
* @param session
* @param source
* @param recordingFormat see
@@ -207,82 +327,133 @@
* @param activeSource
* @param clientEffects
* @param effects
+ * @return null a configuration object.
+ */
+ private AudioRecordingConfiguration createRecordingConfiguration(int uid,
+ int session, int source, int[] recordingInfo, int portId, boolean silenced,
+ int activeSource, AudioEffect.Descriptor[] clientEffects,
+ AudioEffect.Descriptor[] effects) {
+ final AudioFormat clientFormat = new AudioFormat.Builder()
+ .setEncoding(recordingInfo[0])
+ // FIXME this doesn't support index-based masks
+ .setChannelMask(recordingInfo[1])
+ .setSampleRate(recordingInfo[2])
+ .build();
+ final AudioFormat deviceFormat = new AudioFormat.Builder()
+ .setEncoding(recordingInfo[3])
+ // FIXME this doesn't support index-based masks
+ .setChannelMask(recordingInfo[4])
+ .setSampleRate(recordingInfo[5])
+ .build();
+ final int patchHandle = recordingInfo[6];
+ final String[] packages = mPackMan.getPackagesForUid(uid);
+ final String packageName;
+ if (packages != null && packages.length > 0) {
+ packageName = packages[0];
+ } else {
+ packageName = "";
+ }
+ return new AudioRecordingConfiguration(uid, session, source,
+ clientFormat, deviceFormat, patchHandle, packageName,
+ portId, silenced, activeSource, clientEffects, effects);
+ }
+
+ /**
+ * Update the internal "view" of the active recording sessions
+ * @param event RECORD_CONFIG_EVENT_...
+ * @param riid
+ * @param config
* @return null if the list of active recording sessions has not been modified, a list
* with the current active configurations otherwise.
*/
- private List<AudioRecordingConfiguration> updateSnapshot(int event, int uid, int session,
- int source, int[] recordingInfo, int portId, boolean silenced, int activeSource,
- AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects) {
- final boolean configChanged;
- final ArrayList<AudioRecordingConfiguration> configs;
- synchronized(mRecordConfigs) {
- switch (event) {
- case AudioManager.RECORD_CONFIG_EVENT_STOP:
- // return failure if an unknown recording session stopped
- configChanged = (mRecordConfigs.remove(new Integer(portId)) != null);
- if (configChanged) {
- sEventLogger.log(new RecordingEvent(event, uid, session, source, null));
- }
- break;
- case AudioManager.RECORD_CONFIG_EVENT_START:
- final AudioFormat clientFormat = new AudioFormat.Builder()
- .setEncoding(recordingInfo[0])
- // FIXME this doesn't support index-based masks
- .setChannelMask(recordingInfo[1])
- .setSampleRate(recordingInfo[2])
- .build();
- final AudioFormat deviceFormat = new AudioFormat.Builder()
- .setEncoding(recordingInfo[3])
- // FIXME this doesn't support index-based masks
- .setChannelMask(recordingInfo[4])
- .setSampleRate(recordingInfo[5])
- .build();
- final int patchHandle = recordingInfo[6];
- final Integer portIdKey = new Integer(portId);
-
- final String[] packages = mPackMan.getPackagesForUid(uid);
- final String packageName;
- if (packages != null && packages.length > 0) {
- packageName = packages[0];
+ private List<AudioRecordingConfiguration> updateSnapshot(
+ int event, int riid, AudioRecordingConfiguration config) {
+ List<AudioRecordingConfiguration> configs = null;
+ synchronized (mRecordStates) {
+ int stateIndex = -1;
+ if (riid != AudioManager.RECORD_RIID_INVALID) {
+ stateIndex = findStateByRiid(riid);
+ } else if (config != null) {
+ stateIndex = findStateByPortId(config.getClientPortId());
+ }
+ if (stateIndex == -1) {
+ if (event == AudioManager.RECORD_CONFIG_EVENT_START && config != null) {
+ // First time registration for a recorder tracked by AudioServer.
+ mRecordStates.add(new RecordingState(config));
+ stateIndex = mRecordStates.size() - 1;
} else {
- packageName = "";
- }
- final AudioRecordingConfiguration updatedConfig =
- new AudioRecordingConfiguration(uid, session, source,
- clientFormat, deviceFormat, patchHandle, packageName,
- portId, silenced, activeSource, clientEffects, effects);
-
- if (mRecordConfigs.containsKey(portIdKey)) {
- if (updatedConfig.equals(mRecordConfigs.get(portIdKey))) {
- configChanged = false;
- } else {
- // config exists but has been modified
- mRecordConfigs.remove(portIdKey);
- mRecordConfigs.put(portIdKey, updatedConfig);
- configChanged = true;
+ if (config == null) {
+ // Records tracked by clients must be registered first via trackRecorder.
+ Log.e(TAG, String.format(
+ "Unexpected event %d for riid %d", event, riid));
}
- } else {
- mRecordConfigs.put(portIdKey, updatedConfig);
- configChanged = true;
+ return configs;
}
- if (configChanged) {
- sEventLogger.log(new RecordingEvent(event, uid, session, source, packageName));
- }
- break;
- default:
- Log.e(TAG, String.format("Unknown event %d for session %d, source %d",
- event, session, source));
- configChanged = false;
+ }
+ final RecordingState state = mRecordStates.get(stateIndex);
+
+ boolean configChanged;
+ switch (event) {
+ case AudioManager.RECORD_CONFIG_EVENT_START:
+ configChanged = state.setActive(true);
+ if (config != null) { // ??? Can remove ???
+ configChanged = state.setConfig(config) || configChanged;
+ }
+ break;
+ case AudioManager.RECORD_CONFIG_EVENT_UPDATE:
+ // For this event config != null
+ configChanged = state.setConfig(config);
+ break;
+ case AudioManager.RECORD_CONFIG_EVENT_STOP:
+ configChanged = state.setActive(false);
+ if (!state.hasDeathHandler()) {
+ // A recorder tracked by AudioServer has to be removed now so it
+ // does not leak. It will be re-registered if recording starts again.
+ mRecordStates.remove(stateIndex);
+ }
+ break;
+ case AudioManager.RECORD_CONFIG_EVENT_DEATH:
+ configChanged = state.isActiveConfiguration();
+ mRecordStates.remove(stateIndex);
+ break;
+ default:
+ Log.e(TAG, String.format("Unknown event %d for riid %d / portid %d",
+ event, riid, state.getPortId()));
+ configChanged = false;
}
if (configChanged) {
- configs = new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values());
- } else {
- configs = null;
+ sEventLogger.log(new RecordingEvent(event, riid, state.getConfig()));
+ configs = getActiveRecordingConfigurations(true /*isPrivileged*/);
}
}
return configs;
}
+ // riid is assumed to be valid
+ private int findStateByRiid(int riid) {
+ synchronized (mRecordStates) {
+ for (int i = 0; i < mRecordStates.size(); i++) {
+ if (mRecordStates.get(i).getRiid() == riid) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ private int findStateByPortId(int portId) {
+ // Lookup by portId is unambiguous only for recordings managed by the Audio Server.
+ synchronized (mRecordStates) {
+ for (int i = 0; i < mRecordStates.size(); i++) {
+ if (!mRecordStates.get(i).hasDeathHandler()
+ && mRecordStates.get(i).getPortId() == portId) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
/**
* Inner class to track clients that want to be notified of recording updates
*/
@@ -319,28 +490,80 @@
}
}
+ private static final class RecorderDeathHandler implements IBinder.DeathRecipient {
+
+ // can afford to be static because only one RecordingActivityMonitor ever instantiated
+ static RecordingActivityMonitor sMonitor;
+
+ final int mRiid;
+ private final IBinder mRecorderToken;
+
+ RecorderDeathHandler(int riid, IBinder recorderToken) {
+ mRiid = riid;
+ mRecorderToken = recorderToken;
+ }
+
+ public void binderDied() {
+ sMonitor.unregisterRecorder(mRiid);
+ }
+
+ boolean init() {
+ try {
+ mRecorderToken.linkToDeath(this, 0);
+ return true;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not link to recorder death", e);
+ return false;
+ }
+ }
+ }
+
/**
* Inner class for recording event logging
*/
private static final class RecordingEvent extends AudioEventLogger.Event {
private final int mRecEvent;
+ private final int mRIId;
private final int mClientUid;
private final int mSession;
private final int mSource;
private final String mPackName;
- RecordingEvent(int event, int uid, int session, int source, String packName) {
+ RecordingEvent(int event, int riid, AudioRecordingConfiguration config) {
mRecEvent = event;
- mClientUid = uid;
- mSession = session;
- mSource = source;
- mPackName = packName;
+ mRIId = riid;
+ if (config != null) {
+ mClientUid = config.getClientUid();
+ mSession = config.getClientAudioSessionId();
+ mSource = config.getClientAudioSource();
+ mPackName = config.getClientPackageName();
+ } else {
+ mClientUid = -1;
+ mSession = -1;
+ mSource = -1;
+ mPackName = null;
+ }
+ }
+
+ private static String recordEventToString(int recEvent) {
+ switch (recEvent) {
+ case AudioManager.RECORD_CONFIG_EVENT_START:
+ return "start";
+ case AudioManager.RECORD_CONFIG_EVENT_UPDATE:
+ return "update";
+ case AudioManager.RECORD_CONFIG_EVENT_STOP:
+ return "stop";
+ case AudioManager.RECORD_CONFIG_EVENT_DEATH:
+ return "death";
+ default:
+ return "unknown (" + recEvent + ")";
+ }
}
@Override
public String eventToString() {
- return new StringBuilder("rec ").append(
- mRecEvent == AudioManager.RECORD_CONFIG_EVENT_START ? "start" : "stop ")
+ return new StringBuilder("rec ").append(recordEventToString(mRecEvent))
+ .append(" riid:").append(mRIId)
.append(" uid:").append(mClientUid)
.append(" session:").append(mSession)
.append(" src:").append(MediaRecorder.toLogFriendlyAudioSource(mSource))
@@ -349,5 +572,5 @@
}
private static final AudioEventLogger sEventLogger = new AudioEventLogger(50,
- "recording activity as reported through AudioSystem.AudioRecordingCallback");
+ "recording activity received by AudioService");
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index 1c18771..c54bfc0 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -400,10 +400,7 @@
* @throws IOException if there was an issue with local database update.
*/
private void setGenerationId(int userId, int generationId) throws IOException {
- long updatedRows = mDatabase.setPlatformKeyGenerationId(userId, generationId);
- if (updatedRows < 0) {
- throw new IOException("Failed to set the platform key in the local DB.");
- }
+ mDatabase.setPlatformKeyGenerationId(userId, generationId);
}
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index 3f5ac8e..c739650 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -322,19 +322,18 @@
/**
* Sets the {@code generationId} of the platform key for user {@code userId}.
*
- * @return The primary key ID of the relation.
+ * @return The number of updated rows.
*/
public long setPlatformKeyGenerationId(int userId, int generationId) {
SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, userId);
values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID, generationId);
- long result = db.replace(
- UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
- if (result != -1) {
- invalidateKeysWithOldGenerationId(userId, generationId);
- }
- return result;
+ String selection = UserMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
+ String[] selectionArguments = new String[] {String.valueOf(userId)};
+
+ ensureUserMetadataEntryExists(userId);
+ return db.update(UserMetadataEntry.TABLE_NAME, values, selection, selectionArguments);
}
/**
@@ -377,16 +376,19 @@
/**
* Sets the {@code serialNumber} for the user {@code userId}.
*
- * @return The primary key of the inserted row, or -1 if failed.
+ * @return The number of updated rows.
*/
public long setUserSerialNumber(int userId, long serialNumber) {
SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, userId);
values.put(UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER, serialNumber);
- long result = db.replace(
- UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
- return result;
+ String selection = UserMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
+ String[] selectionArguments = new String[] {String.valueOf(userId)};
+
+ ensureUserMetadataEntryExists(userId);
+ return db.update(UserMetadataEntry.TABLE_NAME, values, selection, selectionArguments);
+
}
/**
@@ -1326,6 +1328,18 @@
}
/**
+ * Creates an empty row in the user metadata table if such a row doesn't exist for
+ * the given userId, so db.update will succeed.
+ */
+ private void ensureUserMetadataEntryExists(int userId) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, userId);
+ db.insertWithOnConflict(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
+ values, SQLiteDatabase.CONFLICT_IGNORE);
+ }
+
+ /**
* Closes all open connections to the database.
*/
public void close() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8f1709e..c5a2068 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -546,9 +546,7 @@
private DevicePolicyConstants mConstants;
- private static boolean ENABLE_LOCK_GUARD = Build.IS_ENG
- || true // STOPSHIP Remove it.
- || (SystemProperties.getInt("debug.dpm.lock_guard", 0) == 1);
+ private static final boolean ENABLE_LOCK_GUARD = true;
interface Stats {
int LOCK_GUARD_GUARD = 0;
@@ -1379,7 +1377,7 @@
}
}
- void readFromXml(XmlPullParser parser)
+ void readFromXml(XmlPullParser parser, boolean shouldOverridePolicies)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -1390,7 +1388,10 @@
}
String tag = parser.getName();
if (TAG_POLICIES.equals(tag)) {
- info.readPoliciesFromXml(parser);
+ if (shouldOverridePolicies) {
+ Log.d(LOG_TAG, "Overriding device admin policies from XML.");
+ info.readPoliciesFromXml(parser);
+ }
} else if (TAG_PASSWORD_QUALITY.equals(tag)) {
minimumPasswordMetrics.quality = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
@@ -1518,9 +1519,8 @@
}
} else if (TAG_PARENT_ADMIN.equals(tag)) {
Preconditions.checkState(!isParent);
-
parentAdmin = new ActiveAdmin(info, /* parent */ true);
- parentAdmin.readFromXml(parser);
+ parentAdmin.readFromXml(parser, shouldOverridePolicies);
} else if (TAG_ORGANIZATION_COLOR.equals(tag)) {
organizationColor = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
@@ -3326,8 +3326,10 @@
+ userHandle);
}
if (dai != null) {
+ boolean shouldOverwritePolicies =
+ shouldOverwritePoliciesFromXml(dai.getComponent(), userHandle);
ActiveAdmin ap = new ActiveAdmin(dai, /* parent */ false);
- ap.readFromXml(parser);
+ ap.readFromXml(parser, shouldOverwritePolicies);
policy.mAdminMap.put(ap.info.getComponent(), ap);
}
} catch (RuntimeException e) {
@@ -3437,6 +3439,14 @@
}
}
+ private boolean shouldOverwritePoliciesFromXml(
+ ComponentName deviceAdminComponent, int userHandle) {
+ // http://b/123415062: If DA, overwrite with the stored policies that were agreed by the
+ // user to prevent apps from sneaking additional policies into updates.
+ return !isProfileOwner(deviceAdminComponent, userHandle)
+ && !isDeviceOwner(deviceAdminComponent, userHandle);
+ }
+
private void updateLockTaskPackagesLocked(List<String> packages, int userId) {
long ident = mInjector.binderClearCallingIdentity();
try {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 932a769..bac8414 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -305,6 +305,32 @@
}
@Test
+ public void setUserSerialNumbers_keepsPlatformKeyGenerationId() {
+ int userId = 42;
+ int generationId = 110;
+ Long serialNumber = 10L;
+
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId);
+ mRecoverableKeyStoreDb.setUserSerialNumber(userId, serialNumber);
+
+ assertEquals(generationId, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(userId));
+ }
+
+ @Test
+ public void setPlatformKeyGenerationId_keepsUserSerialNumber() {
+ int userId = 42;
+ int generationId = 110;
+ Long serialNumber = 10L;
+
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId);
+ mRecoverableKeyStoreDb.setUserSerialNumber(userId, serialNumber);
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId + 1);
+
+ assertEquals(serialNumber, mRecoverableKeyStoreDb.getUserSerialNumbers().get(userId));
+ }
+
+
+ @Test
public void removeUserFromAllTables_removesData() throws Exception {
int userId = 12;
int generationId = 24;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 26745a7..2d8a280 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1202,7 +1202,7 @@
* Override the SPN Display Condition 2 integer bits (lsb). B2, B1 is the last two bits of the
* spn display condition coding.
*
- * The default value -1 mean this field is not config.
+ * The default value -1 mean this field is not set.
*
* B1 = 0: display of registered PLMN name not required when registered PLMN is either HPLMN
* or a PLMN in the service provider PLMN list (see EF_SPDI).
@@ -1241,7 +1241,7 @@
/**
* Override the PNN - a string array of comma-separated alpha long and short names:
- * "alpha_long1, alpha_short1".
+ * "alpha_long1,alpha_short1".
*
* Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.58 EF_PNN.
* @hide
@@ -1259,6 +1259,8 @@
/**
* Allow ERI rules to select a carrier name display string when using 3gpp2 access technologies.
+ * If this bit is not set, the carrier name display string will be selected from the carrier
+ * display name resolver which doesn't apply the ERI rules.
*
* @hide
*/