Merge "Add feature check before entering PIP."
diff --git a/Android.bp b/Android.bp
index ee2281f..9645ba6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -113,6 +113,7 @@
"core/java/android/content/IOnPrimaryClipChangedListener.aidl",
"core/java/android/content/IRestrictionsManager.aidl",
"core/java/android/content/ISyncAdapter.aidl",
+ "core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl",
"core/java/android/content/ISyncContext.aidl",
"core/java/android/content/ISyncServiceAdapter.aidl",
"core/java/android/content/ISyncStatusObserver.aidl",
diff --git a/api/current.txt b/api/current.txt
index 206d377..70add65 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8767,6 +8767,7 @@
method public void onSecurityException(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.SyncResult);
method public void onSyncCanceled();
method public void onSyncCanceled(java.lang.Thread);
+ method public boolean onUnsyncableAccount();
field public static final deprecated int LOG_SYNC_DETAILS = 2743; // 0xab7
}
@@ -36518,6 +36519,18 @@
field public static final deprecated java.lang.String WINDOW_ANIMATION_SCALE = "window_animation_scale";
}
+ public class SettingsSlicesContract {
+ field public static final java.lang.String AUTHORITY = "android.settings.slices";
+ field public static final android.net.Uri BASE_URI;
+ field public static final java.lang.String KEY_AIRPLANE_MODE = "airplane_mode";
+ field public static final java.lang.String KEY_BATTERY_SAVER = "battery_saver";
+ field public static final java.lang.String KEY_BLUETOOTH = "bluetooth";
+ field public static final java.lang.String KEY_LOCATION = "location";
+ field public static final java.lang.String KEY_WIFI = "wifi";
+ field public static final java.lang.String PATH_SETTING_ACTION = "action";
+ field public static final java.lang.String PATH_SETTING_INTENT = "intent";
+ }
+
public class SyncStateContract {
ctor public SyncStateContract();
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 6be004c..9127059 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5625,7 +5625,7 @@
field public final java.lang.String description;
field public final boolean isFallback;
field public final java.lang.String packageName;
- field public final java.lang.String[] signatures;
+ field public final android.content.pm.Signature[] signatures;
}
public final class WebViewUpdateService {
diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md
index 9ea6fea..5946515 100644
--- a/cmds/bootanimation/FORMAT.md
+++ b/cmds/bootanimation/FORMAT.md
@@ -30,7 +30,7 @@
It is followed by a number of rows of the form:
- TYPE COUNT PAUSE PATH [#RGBHEX CLOCK]
+ TYPE COUNT PAUSE PATH [#RGBHEX [CLOCK1 [CLOCK2]]]
* **TYPE:** a single char indicating what type of animation segment this is:
+ `p` -- this part will play unless interrupted by the end of the boot
@@ -39,11 +39,38 @@
* **PAUSE:** number of FRAMES to delay after this part ends
* **PATH:** directory in which to find the frames for this part (e.g. `part0`)
* **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB`
- * **CLOCK:** _(OPTIONAL)_ the y-coordinate at which to draw the current time (for watches)
+ * **CLOCK1, CLOCK2:** _(OPTIONAL)_ the coordinates at which to draw the current time (for watches):
+ + If only `CLOCK1` is provided it is the y-coordinate of the clock and the x-coordinate
+ defaults to `c`
+ + If both `CLOCK1` and `CLOCK2` are provided then `CLOCK1` is the x-coordinate and `CLOCK2` is
+ the y-coodinate
+ + Values can be either a positive integer, a negative integer, or `c`
+ - `c` -- will centre the text
+ - `n` -- will position the text n pixels from the start; left edge for x-axis, bottom edge
+ for y-axis
+ - `-n` -- will position the text n pixels from the end; right edge for x-axis, top edge
+ for y-axis
+ - Examples:
+ * `-24` or `c -24` will position the text 24 pixels from the top of the screen,
+ centred horizontally
+ * `16 c` will position the text 16 pixels from the left of the screen, centred
+ vertically
+ * `-32 32` will position the text such that the bottom right corner is 32 pixels above
+ and 32 pixels left of the edges of the screen
There is also a special TYPE, `$SYSTEM`, that loads `/system/media/bootanimation.zip`
and plays that.
+## clock_font.png
+
+The file used to draw the time on top of the boot animation. The font format is as follows:
+ * The file specifies glyphs for the ascii characters 32-127 (0x20-0x7F), both regular weight and
+ bold weight.
+ * The image is divided into a grid of characters
+ * There are 16 columns and 6 rows
+ * Each row is divided in half: regular weight glyphs on the top half, bold glyphs on the bottom
+ * For a NxM image each character glyph will be N/16 pixels wide and M/(12*2) pixels high
+
## loading and playing frames
Each part is scanned and loaded directly from the zip archive. Within a part directory, every file
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 77b156f8..c990296 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -21,7 +21,7 @@
option java_package = "com.android.os";
option java_outer_classname = "AtomsProto";
-import "frameworks/base/core/proto/android/app/activitymanager.proto";
+import "frameworks/base/core/proto/android/app/enums.proto";
/**
* The master atom class. This message defines all of the available
@@ -44,7 +44,7 @@
BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3;
BleScanResultReceived ble_scan_result_received = 4;
SensorStateChanged sensor_state_changed = 5;
- GpsScanStateChanged gps_scan_state_changed = 6; // TODO: untested
+ GpsScanStateChanged gps_scan_state_changed = 6;
SyncStateChanged sync_state_changed = 7;
ScheduledJobStateChanged scheduled_job_state_changed = 8;
ScreenBrightnessChanged screen_brightness_changed = 9;
@@ -185,9 +185,8 @@
message UidProcessStateChanged {
optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
- // The state.
- // TODO: Use the real (mapped) process states.
- optional android.app.ProcessState state = 2;
+ // The state, from frameworks/base/core/proto/android/app/enums.proto.
+ optional android.app.ProcessStateEnum state = 2;
}
/**
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 83b72d9..00d8658 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -63,7 +63,7 @@
}
static string getFilePath(const char* path, int64_t timestamp, int64_t uid, int64_t configID) {
- return StringPrintf("%s/%lld-%d-%lld", path, (long long)timestamp, (int)uid,
+ return StringPrintf("%s/%lld_%d_%lld", path, (long long)timestamp, (int)uid,
(long long)configID);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 039328a..b58c523 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -129,6 +129,8 @@
import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
+import dalvik.system.VMRuntime;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -7113,11 +7115,12 @@
mFragments.dispatchStart();
mFragments.reportLoaderStart();
- // This property is set for all builds except final release
- boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
boolean isAppDebuggable =
(mApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ // This property is set for all builds except final release
+ boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
+
if (isAppDebuggable || isDlwarningEnabled) {
String dlwarning = getDlWarning();
if (dlwarning != null) {
@@ -7138,6 +7141,28 @@
}
}
+ // We might disable this for final builds.
+ boolean isApiWarningEnabled = true;
+
+ if (isAppDebuggable || isApiWarningEnabled) {
+ if (VMRuntime.getRuntime().hasUsedHiddenApi()) {
+ String appName = getApplicationInfo().loadLabel(getPackageManager())
+ .toString();
+ String warning = "Detected problems with API compatiblity\n"
+ + "(please consult log for detail)";
+ if (isAppDebuggable) {
+ new AlertDialog.Builder(this)
+ .setTitle(appName)
+ .setMessage(warning)
+ .setPositiveButton(android.R.string.ok, null)
+ .setCancelable(false)
+ .show();
+ } else {
+ Toast.makeText(this, appName + "\n" + warning, Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
mActivityTransitionState.enterReady(this);
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index fcf8bd7..4d5ac6f 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -575,18 +575,68 @@
/** @hide Process does not exist. */
public static final int PROCESS_STATE_NONEXISTENT = 19;
- // NOTE: If PROCESS_STATEs are added or changed, then new fields must be added
- // to frameworks/base/core/proto/android/app/activitymanager.proto and the following method must
+ // NOTE: If PROCESS_STATEs are added, then new fields must be added
+ // to frameworks/base/core/proto/android/app/enums.proto and the following method must
// be updated to correctly map between them.
+ // However, if the current ActivityManager values are merely modified, no update should be made
+ // to enums.proto, to which values can only be added but never modified. Note that the proto
+ // versions do NOT have the ordering restrictions of the ActivityManager process state.
/**
- * Maps ActivityManager.PROCESS_STATE_ values to ProcessState enum.
+ * Maps ActivityManager.PROCESS_STATE_ values to enums.proto ProcessStateEnum value.
*
* @param amInt a process state of the form ActivityManager.PROCESS_STATE_
- * @return the value of the corresponding ActivityManager's ProcessState enum.
+ * @return the value of the corresponding enums.proto ProcessStateEnum value.
* @hide
*/
public static final int processStateAmToProto(int amInt) {
- return amInt * 100;
+ switch (amInt) {
+ case PROCESS_STATE_UNKNOWN:
+ return AppProtoEnums.PROCESS_STATE_UNKNOWN;
+ case PROCESS_STATE_PERSISTENT:
+ return AppProtoEnums.PROCESS_STATE_PERSISTENT;
+ case PROCESS_STATE_PERSISTENT_UI:
+ return AppProtoEnums.PROCESS_STATE_PERSISTENT_UI;
+ case PROCESS_STATE_TOP:
+ return AppProtoEnums.PROCESS_STATE_TOP;
+ case PROCESS_STATE_FOREGROUND_SERVICE:
+ return AppProtoEnums.PROCESS_STATE_FOREGROUND_SERVICE;
+ case PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
+ return AppProtoEnums.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ case PROCESS_STATE_IMPORTANT_FOREGROUND:
+ return AppProtoEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ case PROCESS_STATE_IMPORTANT_BACKGROUND:
+ return AppProtoEnums.PROCESS_STATE_IMPORTANT_BACKGROUND;
+ case PROCESS_STATE_TRANSIENT_BACKGROUND:
+ return AppProtoEnums.PROCESS_STATE_TRANSIENT_BACKGROUND;
+ case PROCESS_STATE_BACKUP:
+ return AppProtoEnums.PROCESS_STATE_BACKUP;
+ case PROCESS_STATE_SERVICE:
+ return AppProtoEnums.PROCESS_STATE_SERVICE;
+ case PROCESS_STATE_RECEIVER:
+ return AppProtoEnums.PROCESS_STATE_RECEIVER;
+ case PROCESS_STATE_TOP_SLEEPING:
+ return AppProtoEnums.PROCESS_STATE_TOP_SLEEPING;
+ case PROCESS_STATE_HEAVY_WEIGHT:
+ return AppProtoEnums.PROCESS_STATE_HEAVY_WEIGHT;
+ case PROCESS_STATE_HOME:
+ return AppProtoEnums.PROCESS_STATE_HOME;
+ case PROCESS_STATE_LAST_ACTIVITY:
+ return AppProtoEnums.PROCESS_STATE_LAST_ACTIVITY;
+ case PROCESS_STATE_CACHED_ACTIVITY:
+ return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY;
+ case PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+ return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+ case PROCESS_STATE_CACHED_RECENT:
+ return AppProtoEnums.PROCESS_STATE_CACHED_RECENT;
+ case PROCESS_STATE_CACHED_EMPTY:
+ return AppProtoEnums.PROCESS_STATE_CACHED_EMPTY;
+ case PROCESS_STATE_NONEXISTENT:
+ return AppProtoEnums.PROCESS_STATE_NONEXISTENT;
+ default:
+ // ActivityManager process state (amInt)
+ // could not be mapped to an AppProtoEnums ProcessState state.
+ return AppProtoEnums.PROCESS_STATE_UNKNOWN_TO_PROTO;
+ }
}
/** @hide The lowest process state number */
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 5382e66..b25deea 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -146,6 +146,7 @@
void publishService(in IBinder token, in Intent intent, in IBinder service);
void activityResumed(in IBinder token);
void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
+ void setAgentApp(in String packageName, @nullable String agent);
void setAlwaysFinish(boolean enabled);
boolean startInstrumentation(in ComponentName className, in String profileFile,
int flags, in Bundle arguments, in IInstrumentationWatcher watcher,
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index 0ed1b08..6fbe9c6 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -87,6 +87,15 @@
}
/**
+ * Return a new ProfilerInfo instance, with fields populated from this object,
+ * and {@link agent} and {@link attachAgentDuringBind} as given.
+ */
+ public ProfilerInfo setAgent(String agent, boolean attachAgentDuringBind) {
+ return new ProfilerInfo(this.profileFile, this.profileFd, this.samplingInterval,
+ this.autoStopProfiler, this.streamingOutput, agent, attachAgentDuringBind);
+ }
+
+ /**
* Close profileFd, if it is open. The field will be null after a call to this function.
*/
public void closeFd() {
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index 2629929..5a1216b7 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -21,6 +21,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
@@ -166,6 +167,12 @@
private class ISyncAdapterImpl extends ISyncAdapter.Stub {
@Override
+ public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb)
+ throws RemoteException {
+ cb.onUnsyncableAccountDone(AbstractThreadedSyncAdapter.this.onUnsyncableAccount());
+ }
+
+ @Override
public void startSync(ISyncContext syncContext, String authority, Account account,
Bundle extras) {
if (ENABLE_LOG) {
@@ -374,6 +381,26 @@
}
/**
+ * Allows to defer syncing until all accounts are properly set up.
+ *
+ * <p>Called when a account / authority pair
+ * <ul>
+ * <li>that can be handled by this adapter</li>
+ * <li>{@link ContentResolver#requestSync(SyncRequest) is synced}</li>
+ * <li>and the account/provider {@link ContentResolver#getIsSyncable(Account, String) has
+ * unknown state (<0)}.</li>
+ * </ul>
+ *
+ * <p>This might be called on a different service connection as {@link #onPerformSync}.
+ *
+ * @return If {@code false} syncing is deferred. Returns {@code true} by default, i.e. by
+ * default syncing starts immediately.
+ */
+ public boolean onUnsyncableAccount() {
+ return true;
+ }
+
+ /**
* Perform a sync for this account. SyncAdapter-specific parameters may
* be specified in extras, which is guaranteed to not be null. Invocations
* of this method are guaranteed to be serialized.
diff --git a/core/java/android/content/ISyncAdapter.aidl b/core/java/android/content/ISyncAdapter.aidl
index 4660527..0eb581e 100644
--- a/core/java/android/content/ISyncAdapter.aidl
+++ b/core/java/android/content/ISyncAdapter.aidl
@@ -19,6 +19,7 @@
import android.accounts.Account;
import android.os.Bundle;
import android.content.ISyncContext;
+import android.content.ISyncAdapterUnsyncableAccountCallback;
/**
* Interface used to control the sync activity on a SyncAdapter
@@ -26,6 +27,14 @@
*/
oneway interface ISyncAdapter {
/**
+ * Called before {@link #startSync}. This allows the adapter to defer syncs until the
+ * adapter is ready for the account
+ *
+ * @param cb If called back with {@code false} accounts are not synced.
+ */
+ void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb);
+
+ /**
* Initiate a sync for this account. SyncAdapter-specific parameters may
* be specified in extras, which is guaranteed to not be null.
*
diff --git a/core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl b/core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl
new file mode 100644
index 0000000..a738ac2
--- /dev/null
+++ b/core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+/**
+ * Callback for {@link ISyncAdapter#onUnsyncableAccount}
+ * @hide
+ */
+oneway interface ISyncAdapterUnsyncableAccountCallback {
+ /**
+ * Deliver the result for {@link ISyncAdapter#onUnsyncableAccount}
+ *
+ * @param isReady Iff {@code false} account is not synced.
+ */
+ void onUnsyncableAccountDone(boolean isReady);
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 1de8882..fdea5a2 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1246,7 +1246,7 @@
int repeat;
if (effect instanceof VibrationEffect.OneShot) {
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
- pattern = new long[] { 0, oneShot.getTiming() };
+ pattern = new long[] { 0, oneShot.getDuration() };
repeat = -1;
} else if (effect instanceof VibrationEffect.Waveform) {
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7528bc3..a817f33 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -265,6 +265,10 @@
*/
public static final int IME_VISIBLE = 0x2;
+ // Min and max values for back disposition.
+ private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
+ private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_WILL_DISMISS;
+
InputMethodManager mImm;
int mTheme = 0;
@@ -501,9 +505,8 @@
}
clearInsetOfPreviousIme();
// If user uses hard keyboard, IME button should always be shown.
- boolean showing = isInputViewShown();
mImm.setImeWindowStatus(mToken, mStartInputToken,
- IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+ mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
if (resultReceiver != null) {
resultReceiver.send(wasVis != isInputViewShown()
? InputMethodManager.RESULT_SHOWN
@@ -1014,7 +1017,16 @@
}
public void setBackDisposition(int disposition) {
+ if (disposition == mBackDisposition) {
+ return;
+ }
+ if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) {
+ Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified.");
+ return;
+ }
mBackDisposition = disposition;
+ mImm.setImeWindowStatus(mToken, mStartInputToken, mapToImeWindowStatus(isInputViewShown()),
+ mBackDisposition);
}
public int getBackDisposition() {
@@ -1762,7 +1774,7 @@
startExtractingText(false);
}
- final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
+ final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
if (previousImeWindowStatus != nextImeWindowStatus) {
mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
mBackDisposition);
@@ -1889,6 +1901,7 @@
mInputStarted = false;
mStartedInputConnection = null;
mCurCompletions = null;
+ mBackDisposition = BACK_DISPOSITION_DEFAULT;
}
void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
@@ -2104,7 +2117,11 @@
* them to perform navigation in the underlying application.
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
+
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ if (mBackDisposition == BACK_DISPOSITION_WILL_NOT_DISMISS) {
+ return false;
+ }
final ExtractEditText eet = getExtractEditTextIfVisible();
if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
return true;
@@ -2738,6 +2755,10 @@
mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo());
}
+ private static int mapToImeWindowStatus(boolean isInputViewShown) {
+ return IME_ACTIVE | (isInputViewShown ? IME_VISIBLE : 0);
+ }
+
/**
* Performs a dump of the InputMethodService's internal state. Override
* to add your own information to the dump.
diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java
index 2b662a0..2425bba 100644
--- a/core/java/android/net/metrics/NetworkMetrics.java
+++ b/core/java/android/net/metrics/NetworkMetrics.java
@@ -96,6 +96,13 @@
}
}
+ /** Accumulate a single netd sock_diag poll result reported by netd. */
+ public void addTcpStatsResult(int sent, int lost, int rttUs, int sentAckDiffMs) {
+ pendingSummary.tcpLossRate.count(lost, sent);
+ pendingSummary.roundTripTimeUs.count(rttUs);
+ pendingSummary.sentAckTimeDiffenceMs.count(sentAckDiffMs);
+ }
+
/** Represents running sums for dns and connect average error counts and average latencies. */
public static class Summary {
@@ -109,6 +116,13 @@
public final Metrics connectLatencies = new Metrics();
// Blocking and non blocking connect error rate measured in percentage points.
public final Metrics connectErrorRate = new Metrics();
+ // TCP socket packet loss stats collected from Netlink sock_diag.
+ public final Metrics tcpLossRate = new Metrics();
+ // TCP averaged microsecond round-trip-time stats collected from Netlink sock_diag.
+ public final Metrics roundTripTimeUs = new Metrics();
+ // TCP stats collected from Netlink sock_diag that averages millisecond per-socket
+ // differences between last packet sent timestamp and last ack received timestamp.
+ public final Metrics sentAckTimeDiffenceMs = new Metrics();
public Summary(int netId, long transports) {
this.netId = netId;
@@ -120,6 +134,7 @@
dnsErrorRate.merge(that.dnsErrorRate);
connectLatencies.merge(that.connectLatencies);
connectErrorRate.merge(that.connectErrorRate);
+ tcpLossRate.merge(that.tcpLossRate);
}
@Override
@@ -135,6 +150,10 @@
j.add(String.format("connect avg=%dms max=%dms err=%.1f%% tot=%d",
(int) connectLatencies.average(), (int) connectLatencies.max,
100 * connectErrorRate.average(), connectErrorRate.count));
+ j.add(String.format("tcp avg_loss=%.1f%% total_sent=%d total_lost=%d",
+ 100 * tcpLossRate.average(), tcpLossRate.count, (int) tcpLossRate.sum));
+ j.add(String.format("tcp rtt=%dms", (int) (roundTripTimeUs.average() / 1000)));
+ j.add(String.format("tcp sent-ack_diff=%dms", (int) sentAckTimeDiffenceMs.average()));
return j.toString();
}
}
@@ -152,7 +171,11 @@
}
void count(double value) {
- count++;
+ count(value, 1);
+ }
+
+ void count(double value, int subcount) {
+ count += subcount;
sum += value;
max = Math.max(max, value);
}
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index da0ed54..b6f16a7 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -16,7 +16,9 @@
package android.os;
+import android.hardware.vibrator.V1_0.Constants.EffectStrength;
import android.hardware.vibrator.V1_1.Constants.Effect_1_1;
+import android.util.MathUtils;
import java.util.Arrays;
@@ -36,6 +38,12 @@
public static final int DEFAULT_AMPLITUDE = -1;
/**
+ * The maximum amplitude value
+ * @hide
+ */
+ public static final int MAX_AMPLITUDE = 255;
+
+ /**
* A click effect.
*
* @see #get(int)
@@ -198,38 +206,75 @@
/** @hide */
public abstract void validate();
+ /**
+ * Gets the estimated duration of the vibration in milliseconds.
+ *
+ * For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
+ * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where
+ * the length is device and potentially run-time dependent), this returns -1.
+ *
+ * @hide
+ */
+ public abstract long getDuration();
+
+ /**
+ * Scale the amplitude with the given constraints.
+ *
+ * This assumes that the previous value was in the range [0, MAX_AMPLITUDE]
+ * @hide
+ */
+ protected static int scale(int amplitude, float gamma, int maxAmplitude) {
+ float val = MathUtils.pow(amplitude / (float) MAX_AMPLITUDE, gamma);
+ return (int) (val * maxAmplitude);
+ }
+
/** @hide */
public static class OneShot extends VibrationEffect implements Parcelable {
- private long mTiming;
- private int mAmplitude;
+ private final long mDuration;
+ private final int mAmplitude;
public OneShot(Parcel in) {
- this(in.readLong(), in.readInt());
+ mDuration = in.readLong();
+ mAmplitude = in.readInt();
}
public OneShot(long milliseconds, int amplitude) {
- mTiming = milliseconds;
+ mDuration = milliseconds;
mAmplitude = amplitude;
}
- public long getTiming() {
- return mTiming;
+ @Override
+ public long getDuration() {
+ return mDuration;
}
public int getAmplitude() {
return mAmplitude;
}
+ /**
+ * Scale the amplitude of this effect.
+ *
+ * @param gamma the gamma adjustment to apply
+ * @param maxAmplitude the new maximum amplitude of the effect
+ *
+ * @return A {@link OneShot} effect with the same timing but scaled amplitude.
+ */
+ public VibrationEffect scale(float gamma, int maxAmplitude) {
+ int newAmplitude = scale(mAmplitude, gamma, maxAmplitude);
+ return new OneShot(mDuration, newAmplitude);
+ }
+
@Override
public void validate() {
if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
throw new IllegalArgumentException(
- "amplitude must either be DEFAULT_AMPLITUDE, " +
- "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
+ "amplitude must either be DEFAULT_AMPLITUDE, "
+ + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
}
- if (mTiming <= 0) {
+ if (mDuration <= 0) {
throw new IllegalArgumentException(
- "timing must be positive (timing=" + mTiming + ")");
+ "duration must be positive (duration=" + mDuration + ")");
}
}
@@ -239,26 +284,26 @@
return false;
}
VibrationEffect.OneShot other = (VibrationEffect.OneShot) o;
- return other.mTiming == mTiming && other.mAmplitude == mAmplitude;
+ return other.mDuration == mDuration && other.mAmplitude == mAmplitude;
}
@Override
public int hashCode() {
int result = 17;
- result = 37 * (int) mTiming;
- result = 37 * mAmplitude;
+ result += 37 * (int) mDuration;
+ result += 37 * mAmplitude;
return result;
}
@Override
public String toString() {
- return "OneShot{mTiming=" + mTiming +", mAmplitude=" + mAmplitude + "}";
+ return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}";
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_ONE_SHOT);
- out.writeLong(mTiming);
+ out.writeLong(mDuration);
out.writeInt(mAmplitude);
}
@@ -279,9 +324,9 @@
/** @hide */
public static class Waveform extends VibrationEffect implements Parcelable {
- private long[] mTimings;
- private int[] mAmplitudes;
- private int mRepeat;
+ private final long[] mTimings;
+ private final int[] mAmplitudes;
+ private final int mRepeat;
public Waveform(Parcel in) {
this(in.createLongArray(), in.createIntArray(), in.readInt());
@@ -308,34 +353,68 @@
}
@Override
+ public long getDuration() {
+ if (mRepeat >= 0) {
+ return Long.MAX_VALUE;
+ }
+ long duration = 0;
+ for (long d : mTimings) {
+ duration += d;
+ }
+ return duration;
+ }
+
+ /**
+ * Scale the Waveform with the given gamma and new max amplitude.
+ *
+ * @param gamma the gamma adjustment to apply
+ * @param maxAmplitude the new maximum amplitude of the effect
+ *
+ * @return A {@link Waveform} effect with the same timings and repeat index
+ * but scaled amplitude.
+ */
+ public VibrationEffect scale(float gamma, int maxAmplitude) {
+ if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) {
+ // Just return a copy of the original if there's no scaling to be done.
+ return new Waveform(mTimings, mAmplitudes, mRepeat);
+ }
+
+ int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
+ for (int i = 0; i < scaledAmplitudes.length; i++) {
+ scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude);
+ }
+ return new Waveform(mTimings, scaledAmplitudes, mRepeat);
+ }
+
+ @Override
public void validate() {
if (mTimings.length != mAmplitudes.length) {
throw new IllegalArgumentException(
- "timing and amplitude arrays must be of equal length" +
- " (timings.length=" + mTimings.length +
- ", amplitudes.length=" + mAmplitudes.length + ")");
+ "timing and amplitude arrays must be of equal length"
+ + " (timings.length=" + mTimings.length
+ + ", amplitudes.length=" + mAmplitudes.length + ")");
}
if (!hasNonZeroEntry(mTimings)) {
- throw new IllegalArgumentException("at least one timing must be non-zero" +
- " (timings=" + Arrays.toString(mTimings) + ")");
+ throw new IllegalArgumentException("at least one timing must be non-zero"
+ + " (timings=" + Arrays.toString(mTimings) + ")");
}
for (long timing : mTimings) {
if (timing < 0) {
- throw new IllegalArgumentException("timings must all be >= 0" +
- " (timings=" + Arrays.toString(mTimings) + ")");
+ throw new IllegalArgumentException("timings must all be >= 0"
+ + " (timings=" + Arrays.toString(mTimings) + ")");
}
}
for (int amplitude : mAmplitudes) {
if (amplitude < -1 || amplitude > 255) {
throw new IllegalArgumentException(
- "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255" +
- " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
+ "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255"
+ + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
}
}
if (mRepeat < -1 || mRepeat >= mTimings.length) {
throw new IllegalArgumentException(
- "repeat index must be within the bounds of the timings array" +
- " (timings.length=" + mTimings.length + ", index=" + mRepeat +")");
+ "repeat index must be within the bounds of the timings array"
+ + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")");
}
}
@@ -345,26 +424,26 @@
return false;
}
VibrationEffect.Waveform other = (VibrationEffect.Waveform) o;
- return Arrays.equals(mTimings, other.mTimings) &&
- Arrays.equals(mAmplitudes, other.mAmplitudes) &&
- mRepeat == other.mRepeat;
+ return Arrays.equals(mTimings, other.mTimings)
+ && Arrays.equals(mAmplitudes, other.mAmplitudes)
+ && mRepeat == other.mRepeat;
}
@Override
public int hashCode() {
int result = 17;
- result = 37 * Arrays.hashCode(mTimings);
- result = 37 * Arrays.hashCode(mAmplitudes);
- result = 37 * mRepeat;
+ result += 37 * Arrays.hashCode(mTimings);
+ result += 37 * Arrays.hashCode(mAmplitudes);
+ result += 37 * mRepeat;
return result;
}
@Override
public String toString() {
- return "Waveform{mTimings=" + Arrays.toString(mTimings) +
- ", mAmplitudes=" + Arrays.toString(mAmplitudes) +
- ", mRepeat=" + mRepeat +
- "}";
+ return "Waveform{mTimings=" + Arrays.toString(mTimings)
+ + ", mAmplitudes=" + Arrays.toString(mAmplitudes)
+ + ", mRepeat=" + mRepeat
+ + "}";
}
@Override
@@ -402,16 +481,20 @@
/** @hide */
public static class Prebaked extends VibrationEffect implements Parcelable {
- private int mEffectId;
- private boolean mFallback;
+ private final int mEffectId;
+ private final boolean mFallback;
+
+ private int mEffectStrength;
public Prebaked(Parcel in) {
this(in.readInt(), in.readByte() != 0);
+ mEffectStrength = in.readInt();
}
public Prebaked(int effectId, boolean fallback) {
mEffectId = effectId;
mFallback = fallback;
+ mEffectStrength = EffectStrength.MEDIUM;
}
public int getId() {
@@ -427,6 +510,39 @@
}
@Override
+ public long getDuration() {
+ return -1;
+ }
+
+ /**
+ * Set the effect strength of the prebaked effect.
+ */
+ public void setEffectStrength(int strength) {
+ if (!isValidEffectStrength(strength)) {
+ throw new IllegalArgumentException("Invalid effect strength: " + strength);
+ }
+ mEffectStrength = strength;
+ }
+
+ /**
+ * Set the effect strength.
+ */
+ public int getEffectStrength() {
+ return mEffectStrength;
+ }
+
+ private static boolean isValidEffectStrength(int strength) {
+ switch (strength) {
+ case EffectStrength.LIGHT:
+ case EffectStrength.MEDIUM:
+ case EffectStrength.STRONG:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
public void validate() {
switch (mEffectId) {
case EFFECT_CLICK:
@@ -437,6 +553,10 @@
throw new IllegalArgumentException(
"Unknown prebaked effect type (value=" + mEffectId + ")");
}
+ if (!isValidEffectStrength(mEffectStrength)) {
+ throw new IllegalArgumentException(
+ "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
+ }
}
@Override
@@ -445,17 +565,25 @@
return false;
}
VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
- return mEffectId == other.mEffectId && mFallback == other.mFallback;
+ return mEffectId == other.mEffectId
+ && mFallback == other.mFallback
+ && mEffectStrength == other.mEffectStrength;
}
@Override
public int hashCode() {
- return mEffectId;
+ int result = 17;
+ result += 37 * mEffectId;
+ result += 37 * mEffectStrength;
+ return result;
}
@Override
public String toString() {
- return "Prebaked{mEffectId=" + mEffectId + ", mFallback=" + mFallback + "}";
+ return "Prebaked{mEffectId=" + mEffectId
+ + ", mEffectStrength=" + mEffectStrength
+ + ", mFallback=" + mFallback
+ + "}";
}
@@ -464,6 +592,7 @@
out.writeInt(PARCEL_TOKEN_EFFECT);
out.writeInt(mEffectId);
out.writeByte((byte) (mFallback ? 1 : 0));
+ out.writeInt(mEffectStrength);
}
public static final Parcelable.Creator<Prebaked> CREATOR =
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 8078fb8..f1f6f41 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.app.ActivityThread;
@@ -23,6 +24,9 @@
import android.media.AudioAttributes;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Class that operates the vibrator on the device.
* <p>
@@ -33,6 +37,40 @@
public abstract class Vibrator {
private static final String TAG = "Vibrator";
+ /**
+ * Vibration intensity: no vibrations.
+ * @hide
+ */
+ public static final int VIBRATION_INTENSITY_OFF = 0;
+
+ /**
+ * Vibration intensity: low.
+ * @hide
+ */
+ public static final int VIBRATION_INTENSITY_LOW = 1;
+
+ /**
+ * Vibration intensity: medium.
+ * @hide
+ */
+ public static final int VIBRATION_INTENSITY_MEDIUM = 2;
+
+ /**
+ * Vibration intensity: high.
+ * @hide
+ */
+ public static final int VIBRATION_INTENSITY_HIGH = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "VIBRATION_INTENSITY_" }, value = {
+ VIBRATION_INTENSITY_OFF,
+ VIBRATION_INTENSITY_LOW,
+ VIBRATION_INTENSITY_MEDIUM,
+ VIBRATION_INTENSITY_HIGH
+ })
+ public @interface VibrationIntensity{}
+
private final String mPackageName;
/**
@@ -50,6 +88,22 @@
}
/**
+ * Get the default vibration intensity for haptic feedback.
+ * @hide
+ */
+ public int getDefaultHapticFeedbackIntensity() {
+ return VIBRATION_INTENSITY_MEDIUM;
+ }
+
+ /**
+ * Get the default vibration intensity for notifications and ringtones.
+ * @hide
+ */
+ public int getDefaultNotificationVibrationIntensity() {
+ return VIBRATION_INTENSITY_HIGH;
+ }
+
+ /**
* Check whether the hardware has a vibrator.
*
* @return True if the hardware has a vibrator, else false.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e957842..26495cd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3175,6 +3175,43 @@
private static final Validator VIBRATE_INPUT_DEVICES_VALIDATOR = BOOLEAN_VALIDATOR;
/**
+ * The intensity of notification vibrations, if configurable.
+ *
+ * Not all devices are capable of changing their vibration intensity; on these devices
+ * there will likely be no difference between the various vibration intensities except for
+ * intensity 0 (off) and the rest.
+ *
+ * <b>Values:</b><br/>
+ * 0 - Vibration is disabled<br/>
+ * 1 - Weak vibrations<br/>
+ * 2 - Medium vibrations<br/>
+ * 3 - Strong vibrations
+ * @hide
+ */
+ public static final String NOTIFICATION_VIBRATION_INTENSITY =
+ "notification_vibration_intensity";
+
+ /**
+ * The intensity of haptic feedback vibrations, if configurable.
+ *
+ * Not all devices are capable of changing their feedback intensity; on these devices
+ * there will likely be no difference between the various vibration intensities except for
+ * intensity 0 (off) and the rest.
+ *
+ * <b>Values:</b><br/>
+ * 0 - Vibration is disabled<br/>
+ * 1 - Weak vibrations<br/>
+ * 2 - Medium vibrations<br/>
+ * 3 - Strong vibrations
+ * @hide
+ */
+ public static final String HAPTIC_FEEDBACK_INTENSITY =
+ "haptic_feedback_intensity";
+
+ private static final Validator VIBRATION_INTENSITY_VALIDATOR =
+ new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+
+ /**
* Ringer volume. This is used internally, changing this value will not
* change the volume. See AudioManager.
*
@@ -3995,7 +4032,9 @@
LOCK_TO_APP_ENABLED,
NOTIFICATION_SOUND,
ACCELEROMETER_ROTATION,
- SHOW_BATTERY_PERCENT
+ SHOW_BATTERY_PERCENT,
+ NOTIFICATION_VIBRATION_INTENSITY,
+ HAPTIC_FEEDBACK_INTENSITY,
};
/**
@@ -4136,6 +4175,8 @@
VALIDATORS.put(MODE_RINGER_STREAMS_AFFECTED, MODE_RINGER_STREAMS_AFFECTED_VALIDATOR);
VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR);
VALIDATORS.put(VIBRATE_ON, VIBRATE_ON_VALIDATOR);
+ VALIDATORS.put(NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+ VALIDATORS.put(HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(RINGTONE, RINGTONE_VALIDATOR);
VALIDATORS.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_VALIDATOR);
VALIDATORS.put(ALARM_ALERT, ALARM_ALERT_VALIDATOR);
diff --git a/core/java/android/provider/SettingsSlicesContract.java b/core/java/android/provider/SettingsSlicesContract.java
new file mode 100644
index 0000000..f79d852
--- /dev/null
+++ b/core/java/android/provider/SettingsSlicesContract.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+
+/**
+ * Provides a contract for platform-supported Settings {@link android.app.slice.Slice Slices}.
+ * <p>
+ * Contains definitions for the supported {@link android.app.slice.SliceProvider SliceProvider}
+ * authority, authority {@link Uri}, and key constants.
+ * <p>
+ * {@link android.app.slice.Slice Slice} presenters interested in learning meta-data about the
+ * {@link android.app.slice.Slice Slice} should read the {@link android.app.slice.Slice Slice}
+ * object at runtime.
+ * <p>
+ * {@link Uri} builder example:
+ * <pre>
+ * Uri wifiActionUri = AUTHORITY_URI
+ * .buildUpon()
+ * .appendPath(PATH_SETTING_ACTION)
+ * .appendPath(KEY_WIFI)
+ * .build();
+ * Uri bluetoothIntentUri = AUTHORITY_URI
+ * .buildUpon()
+ * .appendPath(PATH_SETTING_INTENT)
+ * .appendPath(KEY_BLUETOOTH)
+ * .build();
+ * </pre>
+ */
+public class SettingsSlicesContract {
+ private SettingsSlicesContract() {
+ }
+
+ /**
+ * Authority for platform Settings Slices.
+ */
+ public static final String AUTHORITY = "android.settings.slices";
+
+ /**
+ * A content:// style uri to the Settings Slices authority, {@link #AUTHORITY}.
+ */
+ public static final Uri BASE_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(AUTHORITY)
+ .build();
+
+ /**
+ * {@link Uri} path indicating that the requested {@link android.app.slice.Slice Slice} should
+ * have inline controls for the corresponding setting.
+ * <p>
+ * This path will only contain Slices defined by keys in this class.
+ */
+ public static final String PATH_SETTING_ACTION = "action";
+
+ /**
+ * {@link Uri} path indicating that the requested {@link android.app.slice.Slice Slice} should
+ * be {@link android.content.Intent Intent}-only.
+ * <p>
+ * {@link android.app.slice.Slice Slices} with actions should use the {@link
+ * #PATH_SETTING_ACTION} path.
+ * <p>
+ * This path will only contain Slices defined by keys in this class
+ */
+ public static final String PATH_SETTING_INTENT = "intent";
+
+ /**
+ * {@link Uri} key for the Airplane Mode setting.
+ */
+ public static final String KEY_AIRPLANE_MODE = "airplane_mode";
+
+ /**
+ * {@link Uri} key for the Battery Saver setting.
+ */
+ public static final String KEY_BATTERY_SAVER = "battery_saver";
+
+ /**
+ * {@link Uri} key for the Bluetooth setting.
+ */
+ public static final String KEY_BLUETOOTH = "bluetooth";
+
+ /**
+ * {@link Uri} key for the Location setting.
+ */
+ public static final String KEY_LOCATION = "location";
+
+ /**
+ * {@link Uri} key for the Wi-fi setting.
+ */
+ public static final String KEY_WIFI = "wifi";
+}
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 5d091c9..b0e9f01 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -17,10 +17,10 @@
package android.webkit;
import android.annotation.SystemApi;
+import android.content.pm.Signature;
import android.os.Parcel;
import android.os.Parcelable;
-
-import java.util.Arrays;
+import android.util.Base64;
/**
* @hide
@@ -34,7 +34,14 @@
this.description = description;
this.availableByDefault = availableByDefault;
this.isFallback = isFallback;
- this.signatures = signatures;
+ if (signatures == null) {
+ this.signatures = new Signature[0];
+ } else {
+ this.signatures = new Signature[signatures.length];
+ for (int n = 0; n < signatures.length; n++) {
+ this.signatures[n] = new Signature(Base64.decode(signatures[n], Base64.DEFAULT));
+ }
+ }
}
// aidl stuff
@@ -54,7 +61,7 @@
description = in.readString();
availableByDefault = (in.readInt() > 0);
isFallback = (in.readInt() > 0);
- signatures = in.createStringArray();
+ signatures = in.createTypedArray(Signature.CREATOR);
}
@Override
@@ -68,7 +75,7 @@
out.writeString(description);
out.writeInt(availableByDefault ? 1 : 0);
out.writeInt(isFallback ? 1 : 0);
- out.writeStringArray(signatures);
+ out.writeTypedArray(signatures, 0);
}
// fields read from framework resource
@@ -76,5 +83,5 @@
public final String description;
public final boolean availableByDefault;
public final boolean isFallback;
- public final String[] signatures;
+ public final Signature[] signatures;
}
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 8650c0a..7f0c086 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -23,6 +23,7 @@
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayerBase;
+import android.media.session.MediaController;
import android.media.update.ApiLoader;
import android.media.update.VideoView2Provider;
import android.media.update.ViewProvider;
@@ -76,8 +77,8 @@
* If a developer wants to attach a customed MediaControlView2, then set enableControlView attribute
* to false and assign the customed media control widget using {@link #setMediaControlView2}.
* <li> VideoView2 is integrated with MediaPlayer2 while VideoView is integrated with MediaPlayer.
- * <li> VideoView2 is integrated with MediaSession2 and so it responses with media key events.
- * A VideoView2 keeps a MediaSession2 instance internally and connects it to a corresponding
+ * <li> VideoView2 is integrated with MediaSession and so it responses with media key events.
+ * A VideoView2 keeps a MediaSession instance internally and connects it to a corresponding
* MediaControlView2 instance.
* </p>
* </ul>
@@ -159,60 +160,18 @@
return mProvider.getMediaControlView2_impl();
}
- /**
- * Starts playback with the media contents specified by {@link #setVideoURI} and
- * {@link #setVideoPath}.
- * If it has been paused, this method will resume playback from the current position.
- */
- public void start() {
- mProvider.start_impl();
- }
/**
- * Pauses playback.
+ * Returns MediaController instance which is connected with MediaSession that VideoView2 is
+ * using. This method should be called when VideoView2 is attached to window, or it throws
+ * IllegalStateException, since internal MediaSession instance is not available until
+ * this view is attached to window. Please check {@link android.view.View#isAttachedToWindow}
+ * before calling this method.
+ *
+ * @throws IllegalStateException if interal MediaSession is not created yet.
*/
- public void pause() {
- mProvider.pause_impl();
- }
-
- /**
- * Gets the duration of the media content specified by #setVideoURI and #setVideoPath
- * in milliseconds.
- */
- public int getDuration() {
- return mProvider.getDuration_impl();
- }
-
- /**
- * Gets current playback position in milliseconds.
- */
- public int getCurrentPosition() {
- return mProvider.getCurrentPosition_impl();
- }
-
- // TODO: mention about key-frame related behavior.
- /**
- * Moves the media by specified time position.
- * @param msec the offset in milliseconds from the start to seek to.
- */
- public void seekTo(int msec) {
- mProvider.seekTo_impl(msec);
- }
-
- /**
- * Says if the media is currently playing.
- * @return true if the media is playing, false if it is not (eg. paused or stopped).
- */
- public boolean isPlaying() {
- return mProvider.isPlaying_impl();
- }
-
- // TODO: check what will return if it is a local media.
- /**
- * Gets the percentage (0-100) of the content that has been buffered or played so far.
- */
- public int getBufferPercentage() {
- return mProvider.getBufferPercentage_impl();
+ public MediaController getMediaController() {
+ return mProvider.getMediaController_impl();
}
/**
@@ -244,7 +203,6 @@
mProvider.setFullScreen_impl(fullScreen);
}
- // TODO: This should be revised after integration with MediaPlayer2.
/**
* Sets playback speed.
*
@@ -254,21 +212,12 @@
* be reset to the normal speed 1.0f.
* @param speed the playback speed. It should be positive.
*/
+ // TODO: Support this via MediaController2.
public void setSpeed(float speed) {
mProvider.setSpeed_impl(speed);
}
/**
- * Returns current speed setting.
- *
- * If setSpeed() has never been called, returns the default value 1.0f.
- * @return current speed setting
- */
- public float getSpeed() {
- return mProvider.getSpeed_impl();
- }
-
- /**
* Sets which type of audio focus will be requested during the playback, or configures playback
* to not request audio focus. Valid values for focus requests are
* {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
@@ -367,14 +316,6 @@
}
/**
- * Stops playback and release all the resources. This should be called whenever a VideoView2
- * instance is no longer to be used.
- */
- public void stopPlayback() {
- mProvider.stopPlayback_impl();
- }
-
- /**
* Registers a callback to be invoked when the media file is loaded and ready to go.
*
* @param l the callback that will be run.
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
index 03f8204..4756c13 100644
--- a/core/proto/android/app/activitymanager.proto
+++ b/core/proto/android/app/activitymanager.proto
@@ -21,65 +21,6 @@
option java_multiple_files = true;
option java_outer_classname = "ActivityManagerProto";
-// ActivityManager.java PROCESS_STATEs
-enum ProcessState {
- // Order matters for process states, so values have been spaced to provide
- // room for future additions.
-
- // Not a real process state.
- PROCESS_STATE_UNKNOWN = -100;
- // Process is a persistent system process.
- PROCESS_STATE_PERSISTENT = 0;
- // Process is a persistent system process and is doing UI.
- PROCESS_STATE_PERSISTENT_UI = 100;
- // Process is hosting the current top activities. Note that this covers
- // all activities that are visible to the user.
- PROCESS_STATE_TOP = 200;
- // Process is hosting a foreground service due to a system binding.
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 300;
- // Process is hosting a foreground service.
- PROCESS_STATE_FOREGROUND_SERVICE = 400;
- // Process is important to the user, and something they are aware of.
- PROCESS_STATE_IMPORTANT_FOREGROUND = 500;
- // Process is important to the user, but not something they are aware of.
- PROCESS_STATE_IMPORTANT_BACKGROUND = 600;
- // Process is in the background transient so we will try to keep running.
- PROCESS_STATE_TRANSIENT_BACKGROUND = 700;
- // Process is in the background running a backup/restore operation.
- PROCESS_STATE_BACKUP = 800;
- // Process is in the background running a service. Unlike oom_adj, this
- // level is used for both the normal running in background state and the
- // executing operations state.
- PROCESS_STATE_SERVICE = 900;
- // Process is in the background running a receiver. Note that from the
- // perspective of oom_adj, receivers run at a higher foreground level, but
- // for our prioritization here that is not necessary and putting them
- // below services means many fewer changes in some process states as they
- // receive broadcasts.
- PROCESS_STATE_RECEIVER = 1000;
- // Same as PROCESS_STATE_TOP but while device is sleeping.
- PROCESS_STATE_TOP_SLEEPING = 1100;
- // Process is in the background, but it can't restore its state so we want
- // to try to avoid killing it.
- PROCESS_STATE_HEAVY_WEIGHT = 1200;
- // Process is in the background but hosts the home activity.
- PROCESS_STATE_HOME = 1300;
- // Process is in the background but hosts the last shown activity.
- PROCESS_STATE_LAST_ACTIVITY = 1400;
- // Process is being cached for later use and contains activities.
- PROCESS_STATE_CACHED_ACTIVITY = 1500;
- // Process is being cached for later use and is a client of another cached
- // process that contains activities.
- PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1600;
- // Process is being cached for later use and has an activity that corresponds
- // to an existing recent task.
- PROCESS_STATE_CACHED_RECENT = 1700;
- // Process is being cached for later use and is empty.
- PROCESS_STATE_CACHED_EMPTY = 1800;
- // Process does not exist.
- PROCESS_STATE_NONEXISTENT = 1900;
-}
-
// ActivityManager.java UID_OBSERVERs flags
enum UidObserverFlag {
// report changes in process state, original value is 1 << 0
diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto
new file mode 100644
index 0000000..2de2574
--- /dev/null
+++ b/core/proto/android/app/enums.proto
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.app;
+
+option java_outer_classname = "AppProtoEnums";
+option java_multiple_files = true;
+
+// ActivityManager.java PROCESS_STATEs
+enum ProcessStateEnum {
+ // Unlike the ActivityManager PROCESS_STATE values, the ordering and numerical values
+ // here are completely fixed and arbitrary. Order is irrelevant.
+ // No attempt need be made to keep them in sync.
+ // The values here must not be modified. Any new process states can be appended to the end.
+
+ // Process state that is unknown to this proto file (i.e. is not mapped
+ // by ActivityManager.processStateAmToProto()). Can only happen if there's a bug in the mapping.
+ PROCESS_STATE_UNKNOWN_TO_PROTO = 998;
+ // Not a real process state.
+ PROCESS_STATE_UNKNOWN = 999;
+ // Process is a persistent system process.
+ PROCESS_STATE_PERSISTENT = 1000;
+ // Process is a persistent system process and is doing UI.
+ PROCESS_STATE_PERSISTENT_UI = 1001;
+ // Process is hosting the current top activities. Note that this covers
+ // all activities that are visible to the user.
+ PROCESS_STATE_TOP = 1002;
+ // Process is hosting a foreground service.
+ PROCESS_STATE_FOREGROUND_SERVICE = 1003;
+ // Process is hosting a foreground service due to a system binding.
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 1004;
+ // Process is important to the user, and something they are aware of.
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 1005;
+ // Process is important to the user, but not something they are aware of.
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 1006;
+ // Process is in the background transient so we will try to keep running.
+ PROCESS_STATE_TRANSIENT_BACKGROUND = 1007;
+ // Process is in the background running a backup/restore operation.
+ PROCESS_STATE_BACKUP = 1008;
+ // Process is in the background running a service. Unlike oom_adj, this
+ // level is used for both the normal running in background state and the
+ // executing operations state.
+ PROCESS_STATE_SERVICE = 1009;
+ // Process is in the background running a receiver. Note that from the
+ // perspective of oom_adj, receivers run at a higher foreground level, but
+ // for our prioritization here that is not necessary and putting them
+ // below services means many fewer changes in some process states as they
+ // receive broadcasts.
+ PROCESS_STATE_RECEIVER = 1010;
+ // Same as PROCESS_STATE_TOP but while device is sleeping.
+ PROCESS_STATE_TOP_SLEEPING = 1011;
+ // Process is in the background, but it can't restore its state so we want
+ // to try to avoid killing it.
+ PROCESS_STATE_HEAVY_WEIGHT = 1012;
+ // Process is in the background but hosts the home activity.
+ PROCESS_STATE_HOME = 1013;
+ // Process is in the background but hosts the last shown activity.
+ PROCESS_STATE_LAST_ACTIVITY = 1014;
+ // Process is being cached for later use and contains activities.
+ PROCESS_STATE_CACHED_ACTIVITY = 1015;
+ // Process is being cached for later use and is a client of another cached
+ // process that contains activities.
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1016;
+ // Process is being cached for later use and has an activity that corresponds
+ // to an existing recent task.
+ PROCESS_STATE_CACHED_RECENT = 1017;
+ // Process is being cached for later use and is empty.
+ PROCESS_STATE_CACHED_EMPTY = 1018;
+ // Process does not exist.
+ PROCESS_STATE_NONEXISTENT = 1019;
+}
+
diff --git a/core/proto/android/content/clipdata.proto b/core/proto/android/content/clipdata.proto
index 6967b69..aeeef97 100644
--- a/core/proto/android/content/clipdata.proto
+++ b/core/proto/android/content/clipdata.proto
@@ -21,13 +21,18 @@
import "frameworks/base/core/proto/android/content/clipdescription.proto";
import "frameworks/base/core/proto/android/content/intent.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
// An android.content.ClipData object.
message ClipDataProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional android.content.ClipDescriptionProto description = 1;
// Custom dump of an android.graphics.Bitmap object.
message Icon {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int32 width = 1;
optional int32 height = 2;
}
@@ -35,6 +40,8 @@
// An android.content.ClipData.Item object.
message Item {
+ option (.android.msg_privacy).dest = DEST_EXPLICIT;
+
oneof data {
string html_text = 1;
string text = 2;
diff --git a/core/proto/android/content/clipdescription.proto b/core/proto/android/content/clipdescription.proto
index 40f4ad3..bc0e940 100644
--- a/core/proto/android/content/clipdescription.proto
+++ b/core/proto/android/content/clipdescription.proto
@@ -20,11 +20,14 @@
option java_multiple_files = true;
import "frameworks/base/core/proto/android/os/persistablebundle.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
// An android.content.ClipDescription object.
message ClipDescriptionProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
repeated string mime_types = 1;
- optional string label = 2;
+ optional string label = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
optional android.os.PersistableBundleProto extras = 3;
optional int64 timestamp_ms = 4;
}
diff --git a/core/proto/android/content/component_name.proto b/core/proto/android/content/component_name.proto
index fc0c8c5..4e49cf2 100644
--- a/core/proto/android/content/component_name.proto
+++ b/core/proto/android/content/component_name.proto
@@ -20,10 +20,14 @@
package android.content;
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
/**
* An android.content.ComponentName object.
*/
message ComponentNameProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional string package_name = 1;
optional string class_name = 2;
}
diff --git a/core/proto/android/net/network.proto b/core/proto/android/net/network.proto
index 9c7ea5d..e13ca9f 100644
--- a/core/proto/android/net/network.proto
+++ b/core/proto/android/net/network.proto
@@ -19,9 +19,14 @@
package android.net;
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
/**
* An android.net.Network object.
*/
message NetworkProto {
- optional int32 net_id = 1;
+ // The netId is an implementation detail which might be changed in the
+ // future, or which alone (i.e. in the absence of some additional context)
+ // might not be sufficient to fully identify a Network.
+ optional int32 net_id = 1 [ (.android.privacy).dest = DEST_AUTOMATIC ];
}
diff --git a/core/proto/android/net/networkcapabilities.proto b/core/proto/android/net/networkcapabilities.proto
index e1c2af1..0338bf8 100644
--- a/core/proto/android/net/networkcapabilities.proto
+++ b/core/proto/android/net/networkcapabilities.proto
@@ -20,10 +20,14 @@
option java_multiple_files = true;
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
/**
* An android.net.NetworkCapabilities object.
*/
message NetworkCapabilitiesProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum Transport {
// Indicates this network uses a Cellular transport.
TRANSPORT_CELLULAR = 0;
@@ -118,7 +122,7 @@
optional int32 link_up_bandwidth_kbps = 3;
optional int32 link_down_bandwidth_kbps = 4;
- optional string network_specifier = 5;
+ optional string network_specifier = 5 [ (.android.privacy).dest = DEST_EXPLICIT ];
// True if this object specifies a signal strength.
optional bool can_report_signal_strength = 6;
diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto
index 9884464..d260b13 100644
--- a/core/proto/android/net/networkrequest.proto
+++ b/core/proto/android/net/networkrequest.proto
@@ -21,11 +21,14 @@
option java_multiple_files = true;
import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
/**
* An android.net.NetworkRequest object.
*/
message NetworkRequestProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum Type {
TYPE_UNKNOWN = 0;
// Only used by applications. When an application creates a
diff --git a/core/proto/android/os/bundle.proto b/core/proto/android/os/bundle.proto
index 6990281..5556936 100644
--- a/core/proto/android/os/bundle.proto
+++ b/core/proto/android/os/bundle.proto
@@ -19,10 +19,14 @@
option java_multiple_files = true;
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
// An android.os.Bundle object.
message BundleProto {
oneof data {
- int32 parcelled_data_size = 1;
- string map_data = 2;
+ int32 parcelled_data_size = 1 [
+ (.android.privacy).dest = DEST_AUTOMATIC
+ ];
+ string map_data = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
}
}
diff --git a/core/proto/android/os/persistablebundle.proto b/core/proto/android/os/persistablebundle.proto
index 75ff787..712f87c 100644
--- a/core/proto/android/os/persistablebundle.proto
+++ b/core/proto/android/os/persistablebundle.proto
@@ -19,10 +19,14 @@
option java_multiple_files = true;
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
// An android.os.PersistableBundle object.
message PersistableBundleProto {
oneof data {
- int32 parcelled_data_size = 1;
- string map_data = 2;
+ int32 parcelled_data_size = 1 [
+ (.android.privacy).dest = DEST_AUTOMATIC
+ ];
+ string map_data = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
}
}
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 39c5ec7..55e6a74 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -19,6 +19,7 @@
package com.android.server.am.proto;
import "frameworks/base/core/proto/android/app/activitymanager.proto";
+import "frameworks/base/core/proto/android/app/enums.proto";
import "frameworks/base/core/proto/android/app/notification.proto";
import "frameworks/base/core/proto/android/app/profilerinfo.proto";
import "frameworks/base/core/proto/android/content/component_name.proto";
@@ -797,7 +798,7 @@
optional string hex_hash = 1;
optional int32 uid = 2;
- optional .android.app.ProcessState current = 3;
+ optional .android.app.ProcessStateEnum current = 3;
optional bool ephemeral = 4;
optional bool fg_services = 5;
optional bool whilelist = 6;
@@ -855,7 +856,7 @@
bool services = 6;
}
- optional .android.app.ProcessState state = 7;
+ optional .android.app.ProcessStateEnum state = 7;
optional int32 trim_memory_level = 8;
optional ProcessRecordProto proc = 9;
optional string adj_type = 10;
@@ -878,8 +879,8 @@
optional int32 set_raw_adj = 3;
optional int32 cur_adj = 4;
optional int32 set_adj = 5;
- optional .android.app.ProcessState current_state = 7;
- optional .android.app.ProcessState set_state = 8;
+ optional .android.app.ProcessStateEnum current_state = 7;
+ optional .android.app.ProcessStateEnum set_state = 8;
optional string last_pss = 9;
optional string last_swap_pss = 10;
optional string last_cached_pss = 11;
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index c9f7d52..5657f96 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -20,8 +20,12 @@
option java_multiple_files = true;
-// Dump from ForceAppStandbyTracker.
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+// Dump from com.android.server.ForceAppStandbyTracker.
message ForceAppStandbyTrackerProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Whether all apps are forced standby or not.
optional bool force_all_apps_standby = 1;
@@ -35,10 +39,11 @@
repeated int32 temp_power_save_whitelist_app_ids = 4;
message RunAnyInBackgroundRestrictedPackages {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int32 uid = 1;
optional string package_name = 2;
}
-
// Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 739fca3..304e63f 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -29,13 +29,18 @@
import "frameworks/base/core/proto/android/os/bundle.proto";
import "frameworks/base/core/proto/android/os/persistablebundle.proto";
import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
message JobSchedulerServiceDumpProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional ConstantsProto settings = 1;
repeated int32 started_users = 2;
message RegisteredJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional JobStatusDumpProto dump = 2;
@@ -56,6 +61,8 @@
// Which uids are currently in the foreground.
message PriorityOverride {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int32 uid = 1;
// Use sint32 instead of an enum since priorities can technically be
// negative.
@@ -71,6 +78,8 @@
optional JobPackageTrackerDumpProto package_tracker = 8;
message PendingJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional JobStatusDumpProto dump = 2;
optional sint32 evaluated_priority = 3;
@@ -81,12 +90,18 @@
// From a service that has currently active or pending jobs.
message ActiveJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
message InactiveJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int64 time_since_stopped_ms = 1;
// This is not always provided.
optional string stopped_reason = 2;
}
message RunningJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
// How long this job has been running for.
optional int64 running_duration_ms = 2;
@@ -119,6 +134,8 @@
// A com.android.server.job.JobSchedulerService.Constants object.
message ConstantsProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Minimum # of idle jobs that must be ready in order to force the JMS to
// schedule things early.
optional int32 min_idle_count = 1;
@@ -187,10 +204,16 @@
}
message StateControllerProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
message AppIdleController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional bool is_parole_on = 1;
message TrackedJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional int32 source_uid = 2;
optional string source_package_name = 3;
@@ -201,9 +224,13 @@
repeated TrackedJob tracked_jobs = 2;
}
message BackgroundJobsController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional com.android.server.ForceAppStandbyTrackerProto force_app_standby_tracker = 1;
message TrackedJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional int32 source_uid = 2;
optional string source_package_name = 3;
@@ -217,6 +244,8 @@
repeated TrackedJob tracked_jobs = 2;
}
message BatteryController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional bool is_on_stable_power = 1;
optional bool is_battery_not_low = 2;
@@ -226,15 +255,21 @@
optional int32 last_broadcast_sequence_number = 4;
message TrackedJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional int32 source_uid = 2;
}
repeated TrackedJob tracked_jobs = 5;
}
message ConnectivityController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional bool is_connected = 1;
message TrackedJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional int32 source_uid = 2;
optional .android.net.NetworkRequestProto required_network = 3;
@@ -242,31 +277,47 @@
repeated TrackedJob tracked_jobs = 2;
}
message ContentObserverController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
message TrackedJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional int32 source_uid = 2;
}
repeated TrackedJob tracked_jobs = 1;
message Observer {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int32 user_id = 1;
message TriggerContentData {
- optional string uri = 1;
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string uri = 1 [
+ (.android.privacy).dest = DEST_EXPLICIT
+ ];
optional int32 flags = 2;
// A
// com.android.server.job.controllers.ContentObserverController.JobInstance
// object.
message JobInstance {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional int32 source_uid = 2;
optional int64 trigger_content_update_delay_ms = 3;
optional int64 trigger_content_max_delay_ms = 4;
- repeated string changed_authorities = 5;
- repeated string changed_uris = 6;
+ repeated string changed_authorities = 5 [
+ (.android.privacy).dest = DEST_EXPLICIT
+ ];
+ repeated string changed_uris = 6 [
+ (.android.privacy).dest = DEST_EXPLICIT
+ ];
}
repeated JobInstance jobs = 3;
}
@@ -275,10 +326,14 @@
repeated Observer observers = 2;
}
message DeviceIdleJobsController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
// True when in device idle mode.
optional bool is_device_idle_mode = 1;
message TrackedJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional int32 source_uid = 2;
optional string source_package_name = 3;
@@ -293,30 +348,42 @@
repeated TrackedJob tracked_jobs = 2;
}
message IdleController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional bool is_idle = 1;
message TrackedJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional int32 source_uid = 2;
}
repeated TrackedJob tracked_jobs = 2;
}
message StorageController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional bool is_storage_not_low = 1;
optional int32 last_broadcast_sequence_number = 2;
message TrackedJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional int32 source_uid = 2;
}
repeated TrackedJob tracked_jobs = 3;
}
message TimeController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int64 now_elapsed_realtime = 1;
optional int64 time_until_next_delay_alarm_ms = 2;
optional int64 time_until_next_deadline_alarm_ms = 3;
message TrackedJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional JobStatusShortInfoProto info = 1;
optional int32 source_uid = 2;
@@ -347,6 +414,8 @@
// A com.android.server.job.JobPackageTracker.DataSet object.
message DataSetProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int64 start_clock_time_ms = 1;
// How much time has elapsed since the DataSet was instantiated.
optional int64 elapsed_time_ms = 2;
@@ -355,10 +424,14 @@
// Represents a com.android.server.job.JobPackageTracker.PackageEntry
// object, but with some extra data.
message PackageEntryProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int32 uid = 1;
optional string package_name = 2;
message State {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int64 duration_ms = 1;
optional int32 count = 2;
}
@@ -377,6 +450,8 @@
optional bool active_top = 8;
message StopReasonCount {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional .android.app.JobParametersProto.CancelReason reason = 1;
optional int32 count = 2;
}
@@ -390,19 +465,27 @@
// Dump from com.android.server.job.GrantedUriPermissions.
message GrantedUriPermissionsDumpProto {
- optional int32 flags = 1;
- optional int32 source_user_id = 2;
+ option (.android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional int32 flags = 1 [ (.android.privacy).dest = DEST_AUTOMATIC ];
+ optional int32 source_user_id = 2 [
+ (.android.privacy).dest = DEST_AUTOMATIC
+ ];
optional string tag = 3;
optional string permission_owner = 4;
repeated string uris = 5;
}
message JobPackageTrackerDumpProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
repeated DataSetProto historical_stats = 1;
optional DataSetProto current_stats = 2;
}
message JobPackageHistoryProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum Event {
UNKNOWN = 0;
START_JOB = 1;
@@ -411,12 +494,14 @@
STOP_PERIODIC_JOB = 4;
}
message HistoryEvent {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional Event event = 1;
optional int64 time_since_event_ms = 2;
optional int32 uid = 3;
// Job IDs can technically be negative.
optional int32 job_id = 4;
- optional string tag = 5;
+ optional string tag = 5 [ (.android.privacy).dest = DEST_EXPLICIT ];
// Only valid for STOP_JOB or STOP_PERIODIC_JOB Events.
optional .android.app.JobParametersProto.CancelReason stop_reason = 6;
}
@@ -424,17 +509,23 @@
}
message JobStatusShortInfoProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int32 calling_uid = 1;
// Job IDs can technically be negative.
optional int32 job_id = 2;
- optional string battery_name = 3;
+ optional string battery_name = 3 [
+ (.android.privacy).dest = DEST_EXPLICIT
+ ];
}
// Dump from a com.android.server.job.controllers.JobStatus object.
message JobStatusDumpProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
// The UID that scheduled the job.
optional int32 calling_uid = 1;
- optional string tag = 2;
+ optional string tag = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
// The UID for which the job is being run.
optional int32 source_uid = 3;
@@ -444,6 +535,8 @@
// Custom dump of android.app.job.JobInfo object.
message JobInfo {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional .android.content.ComponentNameProto service = 1;
optional bool is_periodic = 2;
@@ -461,8 +554,10 @@
optional bool requires_device_idle = 10;
message TriggerContentUri {
- optional int32 flags = 1;
- optional string uri = 2;
+ optional int32 flags = 1 [
+ (.android.privacy).dest = DEST_AUTOMATIC
+ ];
+ optional string uri = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
}
repeated TriggerContentUri trigger_content_uris = 11;
optional int64 trigger_content_update_delay_ms = 12;
@@ -482,6 +577,8 @@
optional int64 max_execution_delay_ms = 21;
message Backoff {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum Policy {
BACKOFF_POLICY_LINEAR = 0;
BACKOFF_POLICY_EXPONENTIAL = 1;
@@ -524,13 +621,19 @@
// Controllers that are currently tracking the job.
repeated TrackingController tracking_controllers = 11;
- repeated string changed_authorities = 12;
- repeated string changed_uris = 13;
+ repeated string changed_authorities = 12 [
+ (.android.privacy).dest = DEST_EXPLICIT
+ ];
+ repeated string changed_uris = 13 [
+ (.android.privacy).dest = DEST_EXPLICIT
+ ];
optional .android.net.NetworkProto network = 14;
// Only the desired data from an android.app.job.JobWorkItem object.
message JobWorkItem {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int32 work_id = 1;
optional int32 delivery_count = 2;
optional .android.content.IntentProto intent = 3;
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index f3ebd41..babbef0 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -19,7 +19,7 @@
option java_multiple_files = true;
-import "frameworks/base/core/proto/android/app/activitymanager.proto";
+import "frameworks/base/core/proto/android/app/enums.proto";
import "frameworks/base/core/proto/android/content/intent.proto";
import "frameworks/base/core/proto/android/os/batterymanager.proto";
import "frameworks/base/core/proto/android/os/looper.proto";
@@ -67,8 +67,7 @@
optional string uid_string = 2;
optional bool is_active = 3;
optional int32 num_wake_locks = 4;
- optional bool is_process_state_unknown = 5;
- optional .android.app.ProcessState process_state = 6;
+ optional .android.app.ProcessStateEnum process_state = 5;
}
optional ConstantsProto constants = 1;
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
index 416ea98..5c05ce4 100644
--- a/media/java/android/media/update/VideoView2Provider.java
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -18,6 +18,7 @@
import android.media.AudioAttributes;
import android.media.MediaPlayerBase;
+import android.media.session.MediaController;
import android.net.Uri;
import android.widget.MediaControlView2;
import android.widget.VideoView2;
@@ -41,20 +42,14 @@
// TODO @SystemApi
public interface VideoView2Provider extends ViewProvider {
void setMediaControlView2_impl(MediaControlView2 mediaControlView);
+ MediaController getMediaController_impl();
MediaControlView2 getMediaControlView2_impl();
- void start_impl();
- void pause_impl();
- int getDuration_impl();
- int getCurrentPosition_impl();
- void seekTo_impl(int msec);
- boolean isPlaying_impl();
- int getBufferPercentage_impl();
int getAudioSessionId_impl();
void showSubtitle_impl();
void hideSubtitle_impl();
void setFullScreen_impl(boolean fullScreen);
+ // TODO: remove setSpeed_impl once MediaController2 is ready.
void setSpeed_impl(float speed);
- float getSpeed_impl();
void setAudioFocusRequest_impl(int focusGain);
void setAudioAttributes_impl(AudioAttributes attributes);
void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player);
@@ -63,7 +58,6 @@
void setVideoURI_impl(Uri uri, Map<String, String> headers);
void setViewType_impl(int viewType);
int getViewType_impl();
- void stopPlayback_impl();
void setOnPreparedListener_impl(VideoView2.OnPreparedListener l);
void setOnCompletionListener_impl(VideoView2.OnCompletionListener l);
void setOnErrorListener_impl(VideoView2.OnErrorListener l);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 74f3aca..051c802 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -108,6 +108,9 @@
"libhidlbase", // VNDK???
"libmediandk", // NDK
"libpowermanager", // for JWakeLock. to be removed
+
+ "libutils", // Have to use shared lib to make libandroid_runtime behave correctly.
+ // Otherwise, AndroidRuntime::getJNIEnv() will return NULL.
],
header_libs: ["libhardware_headers"],
@@ -141,7 +144,6 @@
"libstagefright_rtsp",
"libstagefright_timedtext",
"libunwindstack",
- "libutils",
"libutilscallstack",
"libvndksupport",
"libz",
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 3bf0b37..90ee8a6 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1165,51 +1165,6 @@
;
}
-// Pass through the arguments to the MediaServer player implementation.
-static jint android_media_MediaPlayer2_applyVolumeShaper(JNIEnv *env, jobject thiz,
- jobject jconfig, jobject joperation) {
- // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
- const int VOLUME_SHAPER_INVALID_OPERATION = -38;
-
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == nullptr) {
- return (jint)VOLUME_SHAPER_INVALID_OPERATION;
- }
-
- sp<VolumeShaper::Configuration> configuration;
- sp<VolumeShaper::Operation> operation;
- if (jconfig != nullptr) {
- configuration = VolumeShaperHelper::convertJobjectToConfiguration(
- env, gVolumeShaperFields, jconfig);
- ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
- }
- if (joperation != nullptr) {
- operation = VolumeShaperHelper::convertJobjectToOperation(
- env, gVolumeShaperFields, joperation);
- ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
- }
- VolumeShaper::Status status = mp->applyVolumeShaper(configuration, operation);
- if (status == INVALID_OPERATION) {
- status = VOLUME_SHAPER_INVALID_OPERATION;
- }
- return (jint)status; // if status < 0 an error, else a VolumeShaper id
-}
-
-// Pass through the arguments to the MediaServer player implementation.
-static jobject android_media_MediaPlayer2_getVolumeShaperState(JNIEnv *env, jobject thiz,
- jint id) {
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == nullptr) {
- return (jobject)nullptr;
- }
-
- sp<VolumeShaper::State> state = mp->getVolumeShaperState((int)id);
- if (state.get() == nullptr) {
- return (jobject)nullptr;
- }
- return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
-}
-
/////////////////////////////////////////////////////////////////////////////////////
// Modular DRM begin
@@ -1465,12 +1420,6 @@
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer2_attachAuxEffect},
{"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer2_setRetransmitEndpoint},
{"setNextMediaPlayer", "(Landroid/media/MediaPlayer2;)V", (void *)android_media_MediaPlayer2_setNextMediaPlayer},
- {"native_applyVolumeShaper",
- "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
- (void *)android_media_MediaPlayer2_applyVolumeShaper},
- {"native_getVolumeShaperState",
- "(I)Landroid/media/VolumeShaper$State;",
- (void *)android_media_MediaPlayer2_getVolumeShaperState},
// Modular DRM
{ "_prepareDrm", "([B[B)V", (void *)android_media_MediaPlayer2_prepareDrm },
{ "_releaseDrm", "()V", (void *)android_media_MediaPlayer2_releaseDrm },
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 9ff6815..cb732c4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -51,6 +51,8 @@
import androidx.app.slice.Slice;
import androidx.app.slice.SliceItem;
import androidx.app.slice.core.SliceQuery;
+import androidx.app.slice.widget.ListContent;
+import androidx.app.slice.widget.RowContent;
import androidx.app.slice.widget.SliceLiveData;
/**
@@ -115,25 +117,17 @@
private void showSlice(Slice slice) {
- // Main area
- SliceItem mainItem = SliceQuery.find(slice, android.app.slice.SliceItem.FORMAT_SLICE,
- null /* hints */, new String[]{android.app.slice.Slice.HINT_LIST_ITEM});
- mHasHeader = mainItem != null;
-
- List<SliceItem> subItems = SliceQuery.findAll(slice,
- android.app.slice.SliceItem.FORMAT_SLICE,
- new String[]{android.app.slice.Slice.HINT_LIST_ITEM},
- null /* nonHints */);
-
+ ListContent lc = new ListContent(slice);
+ mHasHeader = lc.hasHeader();
+ List<SliceItem> subItems = lc.getRowItems();
if (!mHasHeader) {
mTitle.setVisibility(GONE);
} else {
mTitle.setVisibility(VISIBLE);
- SliceItem mainTitle = SliceQuery.find(mainItem.getSlice(),
- android.app.slice.SliceItem.FORMAT_TEXT,
- new String[]{android.app.slice.Slice.HINT_TITLE},
- null /* nonHints */);
- CharSequence title = mainTitle.getText();
+ // If there's a header it'll be the first subitem
+ RowContent header = new RowContent(subItems.get(0), true /* showStartItem */);
+ SliceItem mainTitle = header.getTitleItem();
+ CharSequence title = mainTitle != null ? mainTitle.getText() : null;
mTitle.setText(title);
// Check if we're already ellipsizing the text.
@@ -152,9 +146,10 @@
mClickActions.clear();
final int subItemsCount = subItems.size();
final int blendedColor = getTextColor();
-
- for (int i = 0; i < subItemsCount; i++) {
+ final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
+ for (int i = startIndex; i < subItemsCount; i++) {
SliceItem item = subItems.get(i);
+ RowContent rc = new RowContent(item, true /* showStartItem */);
final Uri itemTag = item.getSlice().getUri();
// Try to reuse the view if already exists in the layout
KeyguardSliceButton button = mRow.findViewWithTag(itemTag);
@@ -168,20 +163,13 @@
button.setHasDivider(i < subItemsCount - 1);
mRow.addView(button, i);
- PendingIntent pendingIntent;
- try {
- pendingIntent = item.getAction();
- } catch (RuntimeException e) {
- Log.w(TAG, "Cannot retrieve action from keyguard slice", e);
- pendingIntent = null;
+ PendingIntent pendingIntent = null;
+ if (rc.getContentIntent() != null) {
+ pendingIntent = rc.getContentIntent().getAction();
}
mClickActions.put(button, pendingIntent);
- SliceItem title = SliceQuery.find(item.getSlice(),
- android.app.slice.SliceItem.FORMAT_TEXT,
- new String[]{android.app.slice.Slice.HINT_TITLE},
- null /* nonHints */);
- button.setText(title.getText());
+ button.setText(rc.getTitleItem().getText());
Drawable iconDrawable = null;
SliceItem icon = SliceQuery.find(item.getSlice(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index dec5303..918b6ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -137,8 +137,12 @@
final View transformedView = mTransformedView;
boolean transformX = (transformationFlags & TRANSFORM_X) != 0;
boolean transformY = (transformationFlags & TRANSFORM_Y) != 0;
- boolean differentHeight = otherState.getViewHeight() != getViewHeight();
- boolean differentWidth = otherState.getViewWidth() != getViewWidth();
+ int viewHeight = getViewHeight();
+ int otherHeight = otherState.getViewHeight();
+ boolean differentHeight = otherHeight != viewHeight && otherHeight != 0 && viewHeight != 0;
+ int viewWidth = getViewWidth();
+ int otherWidth = otherState.getViewWidth();
+ boolean differentWidth = otherWidth != viewWidth && otherWidth != 0 && viewWidth != 0;
boolean transformScale = transformScale(otherState) && (differentHeight || differentWidth);
// lets animate the positions correctly
if (transformationAmount == 0.0f
@@ -165,15 +169,15 @@
// we also want to animate the scale if we're the same
View otherView = otherState.getTransformedView();
if (transformScale && differentWidth) {
- setTransformationStartScaleX(otherState.getViewWidth() * otherView.getScaleX()
- / (float) getViewWidth());
+ setTransformationStartScaleX(otherWidth * otherView.getScaleX()
+ / (float) viewWidth);
transformedView.setPivotX(0);
} else {
setTransformationStartScaleX(UNDEFINED);
}
if (transformScale && differentHeight) {
- setTransformationStartScaleY(otherState.getViewHeight() * otherView.getScaleY()
- / (float) getViewHeight());
+ setTransformationStartScaleY(otherHeight * otherView.getScaleY()
+ / (float) viewHeight);
transformedView.setPivotY(0);
} else {
setTransformationStartScaleY(UNDEFINED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 61c8027..5401e74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -300,7 +300,7 @@
boolean showImeSwitcher) {
boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
int hints = mNavigationIconHints;
- if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
+ if (imeShown && backDisposition != InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS) {
hints |= NAVIGATION_HINT_BACK_ALT;
} else {
hints &= ~NAVIGATION_HINT_BACK_ALT;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
index de99d71..c18ed73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
@@ -28,11 +28,12 @@
import android.bluetooth.BluetoothProfile;
import android.net.wifi.WifiManager;
-import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.v7.media.MediaRouter;
import android.telecom.TelecomManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.widget.TextView;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -49,7 +50,8 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class OutputChooserDialogTest extends SysuiTestCase {
OutputChooserDialog mDialog;
@@ -68,7 +70,6 @@
@Before
- @UiThreadTest
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -82,8 +83,12 @@
mDialog = new OutputChooserDialog(getContext(), mRouter);
}
+ @After
+ public void tearDown() throws Exception {
+ TestableLooper.get(this).processAllMessages();
+ }
+
@Test
- @UiThreadTest
public void testClickMediaRouterItemConnectsMedia() {
mDialog.show();
@@ -100,7 +105,6 @@
}
@Test
- @UiThreadTest
public void testClickBtItemConnectsBt() {
mDialog.show();
@@ -116,7 +120,6 @@
}
@Test
- @UiThreadTest
public void testTitleNotInCall() {
mDialog.show();
@@ -126,7 +129,6 @@
}
@Test
- @UiThreadTest
public void testTitleInCall() {
mDialog.setIsInCall(true);
mDialog.show();
@@ -137,7 +139,6 @@
}
@Test
- @UiThreadTest
public void testNoMediaScanIfInCall() {
mDialog.setIsInCall(true);
mDialog.onAttachedToWindow();
@@ -146,7 +147,6 @@
}
@Test
- @UiThreadTest
public void testMediaScanIfNotInCall() {
mDialog.setIsInCall(false);
mDialog.onAttachedToWindow();
@@ -155,7 +155,6 @@
}
@Test
- @UiThreadTest
public void testRegisterCallbacks() {
mDialog.setIsInCall(false);
mDialog.onAttachedToWindow();
@@ -166,7 +165,6 @@
}
@Test
- @UiThreadTest
public void testUnregisterCallbacks() {
mDialog.setIsInCall(false);
mDialog.onDetachedFromWindow();
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index c1cda98..48b5a58 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -73,6 +73,17 @@
private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 };
+ private static final float GAMMA_SCALE_FACTOR_MINIMUM = 2.0f;
+ private static final float GAMMA_SCALE_FACTOR_LOW = 1.5f;
+ private static final float GAMMA_SCALE_FACTOR_HIGH = 0.5f;
+ private static final float GAMMA_SCALE_FACTOR_NONE = 1.0f;
+
+ private static final int MAX_AMPLITUDE_MINIMUM_INTENSITY = 168; // 2/3 * 255
+ private static final int MAX_AMPLITUDE_LOW_INTENSITY = 192; // 3/4 * 255
+
+ // If a vibration is playing for longer than 5s, it's probably not haptic feedback.
+ private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
+
private final LinkedList<VibrationInfo> mPreviousVibrations;
private final int mPreviousVibrationsLimit;
private final boolean mAllowPriorityVibrationsInLowPowerMode;
@@ -89,6 +100,8 @@
private final IBatteryStats mBatteryStatsService;
private PowerManagerInternal mPowerManagerInternal;
private InputManager mIm;
+ private Vibrator mVibrator;
+ private SettingsObserver mSettingObserver;
private volatile VibrateThread mThread;
@@ -101,7 +114,8 @@
private Vibration mCurrentVibration;
private int mCurVibUid = -1;
private boolean mLowPowerMode;
- private SettingsObserver mSettingObserver;
+ private int mHapticFeedbackIntensity;
+ private int mNotificationIntensity;
native static boolean vibratorExists();
native static void vibratorInit();
@@ -112,27 +126,33 @@
native static long vibratorPerformEffect(long effect, long strength);
private class Vibration implements IBinder.DeathRecipient {
- private final IBinder mToken;
- private final VibrationEffect mEffect;
+ public final IBinder token;
// Start time in CLOCK_BOOTTIME base.
- private final long mStartTime;
+ public final long startTime;
// Start time in unix epoch time. Only to be used for debugging purposes and to correlate
- // with other system events, any duration calculations should be done use mStartTime so as
+ // with other system events, any duration calculations should be done use startTime so as
// not to be affected by discontinuities created by RTC adjustments.
- private final long mStartTimeDebug;
- private final int mUsageHint;
- private final int mUid;
- private final String mOpPkg;
+ public final long startTimeDebug;
+ public final int usageHint;
+ public final int uid;
+ public final String opPkg;
+
+ // The actual effect to be played.
+ public VibrationEffect effect;
+ // The original effect that was requested. This is non-null only when the original effect
+ // differs from the effect that's being played. Typically these two things differ because
+ // the effect was scaled based on the users vibration intensity settings.
+ public VibrationEffect originalEffect;
private Vibration(IBinder token, VibrationEffect effect,
int usageHint, int uid, String opPkg) {
- mToken = token;
- mEffect = effect;
- mStartTime = SystemClock.elapsedRealtime();
- mStartTimeDebug = System.currentTimeMillis();
- mUsageHint = usageHint;
- mUid = uid;
- mOpPkg = opPkg;
+ this.token = token;
+ this.effect = effect;
+ this.startTime = SystemClock.elapsedRealtime();
+ this.startTimeDebug = System.currentTimeMillis();
+ this.usageHint = usageHint;
+ this.uid = uid;
+ this.opPkg = opPkg;
}
public void binderDied() {
@@ -143,43 +163,68 @@
}
}
- public boolean hasLongerTimeout(long millis) {
- // If the current effect is a one shot vibration that will end after the given timeout
- // for the new one shot vibration, then just let the current vibration finish. All
- // other effect types will get pre-empted.
- if (mEffect instanceof VibrationEffect.OneShot) {
- VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) mEffect;
- return mStartTime + oneShot.getTiming() > SystemClock.uptimeMillis() + millis;
- }
- return false;
+ public boolean hasTimeoutLongerThan(long millis) {
+ final long duration = effect.getDuration();
+ return duration >= 0 && duration > millis;
}
- public boolean isSystemHapticFeedback() {
- boolean repeating = false;
- if (mEffect instanceof VibrationEffect.Waveform) {
- VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) mEffect;
- repeating = (waveform.getRepeatIndex() < 0);
+ public boolean isHapticFeedback() {
+ if (effect instanceof VibrationEffect.Prebaked) {
+ VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
+ switch (prebaked.getId()) {
+ case VibrationEffect.EFFECT_CLICK:
+ case VibrationEffect.EFFECT_DOUBLE_CLICK:
+ case VibrationEffect.EFFECT_TICK:
+ return true;
+ default:
+ Slog.w(TAG, "Unknown prebaked vibration effect, "
+ + "assuming it isn't haptic feedback.");
+ return false;
+ }
}
- return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg))
- && !repeating;
+ final long duration = effect.getDuration();
+ return duration >= 0 && duration < MAX_HAPTIC_FEEDBACK_DURATION;
+ }
+
+ public boolean isNotification() {
+ switch (usageHint) {
+ case AudioAttributes.USAGE_NOTIFICATION:
+ case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+ case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+ case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public boolean isRingtone() {
+ return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+ }
+
+ public boolean isFromSystem() {
+ return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg);
}
public VibrationInfo toInfo() {
- return new VibrationInfo(mStartTimeDebug, mEffect, mUsageHint, mUid, mOpPkg);
+ return new VibrationInfo(
+ startTimeDebug, effect, originalEffect, usageHint, uid, opPkg);
}
}
private static class VibrationInfo {
private final long mStartTimeDebug;
private final VibrationEffect mEffect;
+ private final VibrationEffect mOriginalEffect;
private final int mUsageHint;
private final int mUid;
private final String mOpPkg;
public VibrationInfo(long startTimeDebug, VibrationEffect effect,
- int usageHint, int uid, String opPkg) {
+ VibrationEffect originalEffect, int usageHint, int uid, String opPkg) {
mStartTimeDebug = startTimeDebug;
mEffect = effect;
+ mOriginalEffect = originalEffect;
mUsageHint = usageHint;
mUid = uid;
mOpPkg = opPkg;
@@ -192,6 +237,8 @@
.append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug)))
.append(", effect: ")
.append(mEffect)
+ .append(", originalEffect: ")
+ .append(mOriginalEffect)
.append(", usageHint: ")
.append(mUsageHint)
.append(", uid: ")
@@ -245,6 +292,7 @@
VibrationEffect tickEffect = createEffect(tickEffectTimings);
mFallbackEffects = new VibrationEffect[] { clickEffect, doubleClickEffect, tickEffect };
+
}
private static VibrationEffect createEffect(long[] timings) {
@@ -259,6 +307,7 @@
public void systemReady() {
mIm = mContext.getSystemService(InputManager.class);
+ mVibrator = mContext.getSystemService(Vibrator.class);
mSettingObserver = new SettingsObserver(mH);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -279,6 +328,14 @@
Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
true, mSettingObserver, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY),
+ true, mSettingObserver, UserHandle.USER_ALL);
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY),
+ true, mSettingObserver, UserHandle.USER_ALL);
+
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -380,11 +437,11 @@
// then just let the current one finish.
if (effect instanceof VibrationEffect.OneShot
&& mCurrentVibration != null
- && mCurrentVibration.mEffect instanceof VibrationEffect.OneShot) {
+ && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
VibrationEffect.OneShot currentOneShot =
- (VibrationEffect.OneShot) mCurrentVibration.mEffect;
- if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming())
+ (VibrationEffect.OneShot) mCurrentVibration.effect;
+ if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
&& newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
@@ -398,7 +455,7 @@
// to grab the attention of the user, like ringtones and alarms, in favor of one-shot
// vibrations that are likely quite short.
if (!isRepeatingVibration(effect)
- && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.mEffect)) {
+ && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.effect)) {
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
}
@@ -421,13 +478,7 @@
}
private static boolean isRepeatingVibration(VibrationEffect effect) {
- if (effect instanceof VibrationEffect.Waveform) {
- final VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
- if (waveform.getRepeatIndex() >= 0) {
- return true;
- }
- }
- return false;
+ return effect.getDuration() == Long.MAX_VALUE;
}
private void addToPreviousVibrationsLocked(Vibration vib) {
@@ -444,7 +495,7 @@
"cancelVibrate");
synchronized (mLock) {
- if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
+ if (mCurrentVibration != null && mCurrentVibration.token == token) {
if (DEBUG) {
Slog.d(TAG, "Canceling vibration.");
}
@@ -488,15 +539,16 @@
}
private void startVibrationLocked(final Vibration vib) {
- if (!isAllowedToVibrate(vib)) {
- if (DEBUG) {
- Slog.e(TAG, "Vibrate ignored, low power mode");
- }
+ if (!isAllowedToVibrateLocked(vib)) {
return;
}
- if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE &&
- !shouldVibrateForRingtone()) {
+ final int intensity = getCurrentIntensityLocked(vib);
+ if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ return;
+ }
+
+ if (vib.isRingtone() && !shouldVibrateForRingtone()) {
if (DEBUG) {
Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
}
@@ -508,26 +560,27 @@
if (mode == AppOpsManager.MODE_ERRORED) {
// We might be getting calls from within system_server, so we don't actually want
// to throw a SecurityException here.
- Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
+ Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
}
return;
}
+ applyVibrationIntensityScalingLocked(vib, intensity);
startVibrationInnerLocked(vib);
}
private void startVibrationInnerLocked(Vibration vib) {
mCurrentVibration = vib;
- if (vib.mEffect instanceof VibrationEffect.OneShot) {
- VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.mEffect;
- doVibratorOn(oneShot.getTiming(), oneShot.getAmplitude(), vib.mUid, vib.mUsageHint);
- mH.postDelayed(mVibrationEndRunnable, oneShot.getTiming());
- } else if (vib.mEffect instanceof VibrationEffect.Waveform) {
+ if (vib.effect instanceof VibrationEffect.OneShot) {
+ VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
+ doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
+ mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
+ } else if (vib.effect instanceof VibrationEffect.Waveform) {
// mThread better be null here. doCancelVibrate should always be
// called before startNextVibrationLocked or startVibrationLocked.
- VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.mEffect;
- mThread = new VibrateThread(waveform, vib.mUid, vib.mUsageHint);
+ VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
+ mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
mThread.start();
- } else if (vib.mEffect instanceof VibrationEffect.Prebaked) {
+ } else if (vib.effect instanceof VibrationEffect.Prebaked) {
long timeout = doVibratorPrebakedEffectLocked(vib);
if (timeout > 0) {
mH.postDelayed(mVibrationEndRunnable, timeout);
@@ -537,28 +590,91 @@
}
}
- private boolean isAllowedToVibrate(Vibration vib) {
+ private boolean isAllowedToVibrateLocked(Vibration vib) {
if (!mLowPowerMode) {
return true;
}
- if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
+
+ if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
return true;
}
- if (!mAllowPriorityVibrationsInLowPowerMode) {
- return false;
- }
- if (vib.mUsageHint == AudioAttributes.USAGE_ALARM ||
- vib.mUsageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
- vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
+ if (vib.usageHint == AudioAttributes.USAGE_ALARM ||
+ vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
+ vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
return true;
}
return false;
}
+ private int getCurrentIntensityLocked(Vibration vib) {
+ if (vib.isNotification() || vib.isRingtone()){
+ return mNotificationIntensity;
+ } else if (vib.isHapticFeedback()) {
+ return mHapticFeedbackIntensity;
+ } else {
+ return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+ }
+ }
+
+ /**
+ * Scale the vibration effect by the intensity as appropriate based its intent.
+ */
+ private void applyVibrationIntensityScalingLocked(Vibration vib, int intensity) {
+ if (vib.effect instanceof VibrationEffect.Prebaked) {
+ // Prebaked effects are always just a direct translation from intensity to
+ // EffectStrength.
+ VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked)vib.effect;
+ prebaked.setEffectStrength(intensityToEffectStrength(intensity));
+ return;
+ }
+
+ final float gamma;
+ final int maxAmplitude;
+ if (vib.isNotification() || vib.isRingtone()) {
+ if (intensity == Vibrator.VIBRATION_INTENSITY_LOW) {
+ gamma = GAMMA_SCALE_FACTOR_MINIMUM;
+ maxAmplitude = MAX_AMPLITUDE_MINIMUM_INTENSITY;
+ } else if (intensity == Vibrator.VIBRATION_INTENSITY_MEDIUM) {
+ gamma = GAMMA_SCALE_FACTOR_LOW;
+ maxAmplitude = MAX_AMPLITUDE_LOW_INTENSITY;
+ } else {
+ gamma = GAMMA_SCALE_FACTOR_NONE;
+ maxAmplitude = VibrationEffect.MAX_AMPLITUDE;
+ }
+ } else {
+ if (intensity == Vibrator.VIBRATION_INTENSITY_LOW) {
+ gamma = GAMMA_SCALE_FACTOR_LOW;
+ maxAmplitude = MAX_AMPLITUDE_LOW_INTENSITY;
+ } else if (intensity == Vibrator.VIBRATION_INTENSITY_HIGH) {
+ gamma = GAMMA_SCALE_FACTOR_HIGH;
+ maxAmplitude = VibrationEffect.MAX_AMPLITUDE;
+ } else {
+ gamma = GAMMA_SCALE_FACTOR_NONE;
+ maxAmplitude = VibrationEffect.MAX_AMPLITUDE;
+ }
+ }
+
+ VibrationEffect scaledEffect = null;
+ if (vib.effect instanceof VibrationEffect.OneShot) {
+ VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
+ scaledEffect = oneShot.scale(gamma, maxAmplitude);
+ } else if (vib.effect instanceof VibrationEffect.Waveform) {
+ VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
+ scaledEffect = waveform.scale(gamma, maxAmplitude);
+ } else {
+ Slog.w(TAG, "Unable to apply intensity scaling, unknown VibrationEffect type");
+ }
+
+ if (scaledEffect != null) {
+ vib.originalEffect = vib.effect;
+ vib.effect = scaledEffect;
+ }
+ }
+
private boolean shouldVibrateForRingtone() {
- AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ AudioManager audioManager = mContext.getSystemService(AudioManager.class);
int ringerMode = audioManager.getRingerModeInternal();
// "Also vibrate for calls" Setting in Sound
if (Settings.System.getInt(
@@ -573,10 +689,10 @@
int mode;
try {
mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
- vib.mUsageHint, vib.mUid, vib.mOpPkg);
+ vib.usageHint, vib.uid, vib.opPkg);
if (mode == AppOpsManager.MODE_ALLOWED) {
mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg);
+ AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get appop mode for vibration!", e);
@@ -589,8 +705,8 @@
if (mCurrentVibration != null) {
try {
mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
- AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
- mCurrentVibration.mOpPkg);
+ AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
+ mCurrentVibration.opPkg);
} catch (RemoteException e) { }
unlinkVibration(mCurrentVibration);
mCurrentVibration = null;
@@ -600,9 +716,9 @@
private void linkVibration(Vibration vib) {
// Only link against waveforms since they potentially don't have a finish if
// they're repeating. Let other effects just play out until they're done.
- if (vib.mEffect instanceof VibrationEffect.Waveform) {
+ if (vib.effect instanceof VibrationEffect.Waveform) {
try {
- vib.mToken.linkToDeath(vib, 0);
+ vib.token.linkToDeath(vib, 0);
} catch (RemoteException e) {
return;
}
@@ -610,8 +726,8 @@
}
private void unlinkVibration(Vibration vib) {
- if (vib.mEffect instanceof VibrationEffect.Waveform) {
- vib.mToken.unlinkToDeath(vib, 0);
+ if (vib.effect instanceof VibrationEffect.Waveform) {
+ vib.token.unlinkToDeath(vib, 0);
}
}
@@ -619,6 +735,7 @@
synchronized (mLock) {
boolean devicesUpdated = updateInputDeviceVibratorsLocked();
boolean lowPowerModeUpdated = updateLowPowerModeLocked();
+ updateVibrationIntensityLocked();
if (devicesUpdated || lowPowerModeUpdated) {
// If the state changes out from under us then just reset.
@@ -678,6 +795,15 @@
return false;
}
+ private void updateVibrationIntensityLocked() {
+ mHapticFeedbackIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ mVibrator.getDefaultHapticFeedbackIntensity(), UserHandle.USER_CURRENT);
+ mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT);
+ }
+
@Override
public void onInputDeviceAdded(int deviceId) {
updateVibrators();
@@ -755,28 +881,32 @@
}
private long doVibratorPrebakedEffectLocked(Vibration vib) {
+ final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
+ final boolean usingInputDeviceVibrators;
synchronized (mInputDeviceVibrators) {
- VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.mEffect;
- // Input devices don't support prebaked effect, so skip trying it with them.
- if (mInputDeviceVibrators.isEmpty()) {
- long timeout = vibratorPerformEffect(prebaked.getId(), EffectStrength.MEDIUM);
- if (timeout > 0) {
- noteVibratorOnLocked(vib.mUid, timeout);
- return timeout;
- }
- }
- if (!prebaked.shouldFallback()) {
- return 0;
- }
- VibrationEffect effect = getFallbackEffect(prebaked.getId());
- if (effect == null) {
- Slog.w(TAG, "Failed to play prebaked effect, no fallback");
- return 0;
- }
- Vibration fallbackVib =
- new Vibration(vib.mToken, effect, vib.mUsageHint, vib.mUid, vib.mOpPkg);
- startVibrationInnerLocked(fallbackVib);
+ usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
}
+ // Input devices don't support prebaked effect, so skip trying it with them.
+ if (!usingInputDeviceVibrators) {
+ long timeout = vibratorPerformEffect(prebaked.getId(), prebaked.getEffectStrength());
+ if (timeout > 0) {
+ noteVibratorOnLocked(vib.uid, timeout);
+ return timeout;
+ }
+ }
+ if (!prebaked.shouldFallback()) {
+ return 0;
+ }
+ VibrationEffect effect = getFallbackEffect(prebaked.getId());
+ if (effect == null) {
+ Slog.w(TAG, "Failed to play prebaked effect, no fallback");
+ return 0;
+ }
+ Vibration fallbackVib =
+ new Vibration(vib.token, effect, vib.usageHint, vib.uid, vib.opPkg);
+ final int intensity = getCurrentIntensityLocked(fallbackVib);
+ applyVibrationIntensityScalingLocked(fallbackVib, intensity);
+ startVibrationInnerLocked(fallbackVib);
return 0;
}
@@ -787,6 +917,25 @@
return mFallbackEffects[effectId];
}
+ /**
+ * Return the current desired effect strength.
+ *
+ * If the returned value is < 0 then the vibration shouldn't be played at all.
+ */
+ private static int intensityToEffectStrength(int intensity) {
+ switch (intensity) {
+ case Vibrator.VIBRATION_INTENSITY_LOW:
+ return EffectStrength.LIGHT;
+ case Vibrator.VIBRATION_INTENSITY_MEDIUM:
+ return EffectStrength.MEDIUM;
+ case Vibrator.VIBRATION_INTENSITY_HIGH:
+ return EffectStrength.STRONG;
+ default:
+ Slog.w(TAG, "Got unexpected vibration intensity: " + intensity);
+ return EffectStrength.STRONG;
+ }
+ }
+
private void noteVibratorOnLocked(int uid, long millis) {
try {
mBatteryStatsService.noteVibratorOn(uid, millis);
@@ -945,7 +1094,8 @@
// haptic feedback as part of the transition. So we don't cancel
// system vibrations.
if (mCurrentVibration != null
- && !mCurrentVibration.isSystemHapticFeedback()) {
+ && !(mCurrentVibration.isHapticFeedback()
+ && mCurrentVibration.isFromSystem())) {
doCancelVibrateLocked();
}
}
@@ -957,10 +1107,21 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- pw.println("Previous vibrations:");
+ pw.println("Vibrator Service:");
synchronized (mLock) {
+ pw.print(" mCurrentVibration=");
+ if (mCurrentVibration != null) {
+ pw.println(mCurrentVibration.toInfo().toString());
+ } else {
+ pw.println("null");
+ }
+ pw.println(" mLowPowerMode=" + mLowPowerMode);
+ pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
+ pw.println(" mNotificationIntensity=" + mNotificationIntensity);
+ pw.println("");
+ pw.println(" Previous vibrations:");
for (VibrationInfo info : mPreviousVibrations) {
- pw.print(" ");
+ pw.print(" ");
pw.println(info.toString());
}
}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index db21ef1..427ccba 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -410,7 +410,7 @@
}
} finally {
final ActivityStack topFullscreenStack =
- getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
if (topFullscreenStack != null && mHomeStack != null && !isTopStack(mHomeStack)) {
// Whenever split-screen is dismissed we want the home stack directly behind the
// current top fullscreen stack so it shows up when the top stack is finished.
@@ -566,6 +566,16 @@
return false;
}
+ ActivityStack getTopStackInWindowingMode(int windowingMode) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack current = mStacks.get(i);
+ if (windowingMode == current.getWindowingMode()) {
+ return current;
+ }
+ }
+ return null;
+ }
+
int getIndexOf(ActivityStack stack) {
return mStacks.indexOf(stack);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f469437..168baf0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -46,6 +46,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -1634,6 +1635,14 @@
String mProfileApp = null;
ProcessRecord mProfileProc = null;
ProfilerInfo mProfilerInfo = null;
+
+ /**
+ * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
+ * is not null, this map is checked and the mapped agent installed during bind-time. Note:
+ * A non-null agent in mProfileInfo overrides this.
+ */
+ private @Nullable Map<String, String> mAppAgentMap = null;
+
int mProfileType = 0;
final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
String mMemWatchDumpProcName;
@@ -7453,25 +7462,6 @@
}
}
- ProfilerInfo profilerInfo = null;
- String preBindAgent = null;
- if (mProfileApp != null && mProfileApp.equals(processName)) {
- mProfileProc = app;
- if (mProfilerInfo != null) {
- // Send a profiler info object to the app if either a file is given, or
- // an agent should be loaded at bind-time.
- boolean needsInfo = mProfilerInfo.profileFile != null
- || mProfilerInfo.attachAgentDuringBind;
- profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
- if (!mProfilerInfo.attachAgentDuringBind) {
- preBindAgent = mProfilerInfo.agent;
- }
- }
- } else if (app.instr != null && app.instr.mProfileFile != null) {
- profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
- null, false);
- }
-
boolean enableTrackAllocation = false;
if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
enableTrackAllocation = true;
@@ -7496,6 +7486,39 @@
ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
app.compat = compatibilityInfoForPackageLocked(appInfo);
+ ProfilerInfo profilerInfo = null;
+ String preBindAgent = null;
+ if (mProfileApp != null && mProfileApp.equals(processName)) {
+ mProfileProc = app;
+ if (mProfilerInfo != null) {
+ // Send a profiler info object to the app if either a file is given, or
+ // an agent should be loaded at bind-time.
+ boolean needsInfo = mProfilerInfo.profileFile != null
+ || mProfilerInfo.attachAgentDuringBind;
+ profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
+ if (mProfilerInfo.agent != null) {
+ preBindAgent = mProfilerInfo.agent;
+ }
+ }
+ } else if (app.instr != null && app.instr.mProfileFile != null) {
+ profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
+ null, false);
+ }
+ if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
+ // We need to do a debuggable check here. See setAgentApp for why the check is
+ // postponed to here.
+ if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ String agent = mAppAgentMap.get(processName);
+ // Do not overwrite already requested agent.
+ if (profilerInfo == null) {
+ profilerInfo = new ProfilerInfo(null, null, 0, false, false,
+ mAppAgentMap.get(processName), true);
+ } else if (profilerInfo.agent == null) {
+ profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
+ }
+ }
+ }
+
if (profilerInfo != null && profilerInfo.profileFd != null) {
profilerInfo.profileFd = profilerInfo.profileFd.dup();
}
@@ -11042,8 +11065,20 @@
}
if (toTop) {
+ // Caller wants the current split-screen primary stack to be the top stack after
+ // it goes fullscreen, so move it to the front.
stack.moveToFront("dismissSplitScreenMode");
+ } else if (mStackSupervisor.isFocusedStack(stack)) {
+ // In this case the current split-screen primary stack shouldn't be the top
+ // stack after it goes fullscreen, but it current has focus, so we move the
+ // focus to the top-most split-screen secondary stack next to it.
+ final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ if (otherStack != null) {
+ otherStack.moveToFront("dismissSplitScreenMode_other");
+ }
}
+
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
}
} finally {
@@ -13152,6 +13187,52 @@
}
}
+ /**
+ * Set or remove an agent to be run whenever an app with the given process name starts.
+ *
+ * This method will not check whether the given process name matches a debuggable app. That
+ * would require scanning all current packages, and a rescan when new packages are installed
+ * or updated.
+ *
+ * Instead, do the check when an application is started and matched to a stored agent.
+ *
+ * @param packageName the process name of the app.
+ * @param agent the agent string to be used, or null to remove any previously set agent.
+ */
+ @Override
+ public void setAgentApp(@NonNull String packageName, @Nullable String agent) {
+ synchronized (this) {
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission.
+ if (checkCallingPermission(
+ android.Manifest.permission.SET_ACTIVITY_WATCHER) !=
+ PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+
+ if (agent == null) {
+ if (mAppAgentMap != null) {
+ mAppAgentMap.remove(packageName);
+ if (mAppAgentMap.isEmpty()) {
+ mAppAgentMap = null;
+ }
+ }
+ } else {
+ if (mAppAgentMap == null) {
+ mAppAgentMap = new HashMap<>();
+ }
+ if (mAppAgentMap.size() >= 100) {
+ // Limit the size of the map, to avoid OOMEs.
+ Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
+ + "/" + agent);
+ return;
+ }
+ mAppAgentMap.put(packageName, agent);
+ }
+ }
+ }
+
void setTrackAllocationApp(ApplicationInfo app, String processName) {
synchronized (this) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 1240f5e..f0c90e0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -160,6 +160,8 @@
return runDumpHeap(pw);
case "set-debug-app":
return runSetDebugApp(pw);
+ case "set-agent-app":
+ return runSetAgentApp(pw);
case "clear-debug-app":
return runClearDebugApp(pw);
case "set-watch-heap":
@@ -873,6 +875,13 @@
return 0;
}
+ int runSetAgentApp(PrintWriter pw) throws RemoteException {
+ String pkg = getNextArgRequired();
+ String agent = getNextArg();
+ mInterface.setAgentApp(pkg, agent);
+ return 0;
+ }
+
int runClearDebugApp(PrintWriter pw) throws RemoteException {
mInterface.setDebugApp(null, false, true);
return 0;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 6b380f1..0d96468 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.app.ActivityManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -353,10 +354,12 @@
}
}
+ /** @param state Process state from ActivityManager.java. */
void noteUidProcessState(int uid, int state) {
synchronized (mStats) {
// TODO: remove this once we figure out properly where and how
- StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid, state);
+ StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid,
+ ActivityManager.processStateAmToProto(state));
mStats.noteUidProcessStateLocked(uid, state);
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 29bfebe..a50d069 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -24,7 +24,7 @@
import java.nio.ByteBuffer;
import android.app.ActivityManager;
-import android.app.ActivityManagerProto;
+import android.app.AppProtoEnums;
import android.os.Build;
import android.os.SystemClock;
import com.android.internal.util.MemInfoReader;
@@ -420,47 +420,49 @@
public static int makeProcStateProtoEnum(int curProcState) {
switch (curProcState) {
case ActivityManager.PROCESS_STATE_PERSISTENT:
- return ActivityManagerProto.PROCESS_STATE_PERSISTENT;
+ return AppProtoEnums.PROCESS_STATE_PERSISTENT;
case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
- return ActivityManagerProto.PROCESS_STATE_PERSISTENT_UI;
+ return AppProtoEnums.PROCESS_STATE_PERSISTENT_UI;
case ActivityManager.PROCESS_STATE_TOP:
- return ActivityManagerProto.PROCESS_STATE_TOP;
+ return AppProtoEnums.PROCESS_STATE_TOP;
case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
- return ActivityManagerProto.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ return AppProtoEnums.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
- return ActivityManagerProto.PROCESS_STATE_FOREGROUND_SERVICE;
+ return AppProtoEnums.PROCESS_STATE_FOREGROUND_SERVICE;
case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
- return ActivityManagerProto.PROCESS_STATE_TOP_SLEEPING;
+ return AppProtoEnums.PROCESS_STATE_TOP_SLEEPING;
case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
- return ActivityManagerProto.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ return AppProtoEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
- return ActivityManagerProto.PROCESS_STATE_IMPORTANT_BACKGROUND;
+ return AppProtoEnums.PROCESS_STATE_IMPORTANT_BACKGROUND;
case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
- return ActivityManagerProto.PROCESS_STATE_TRANSIENT_BACKGROUND;
+ return AppProtoEnums.PROCESS_STATE_TRANSIENT_BACKGROUND;
case ActivityManager.PROCESS_STATE_BACKUP:
- return ActivityManagerProto.PROCESS_STATE_BACKUP;
+ return AppProtoEnums.PROCESS_STATE_BACKUP;
case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
- return ActivityManagerProto.PROCESS_STATE_HEAVY_WEIGHT;
+ return AppProtoEnums.PROCESS_STATE_HEAVY_WEIGHT;
case ActivityManager.PROCESS_STATE_SERVICE:
- return ActivityManagerProto.PROCESS_STATE_SERVICE;
+ return AppProtoEnums.PROCESS_STATE_SERVICE;
case ActivityManager.PROCESS_STATE_RECEIVER:
- return ActivityManagerProto.PROCESS_STATE_RECEIVER;
+ return AppProtoEnums.PROCESS_STATE_RECEIVER;
case ActivityManager.PROCESS_STATE_HOME:
- return ActivityManagerProto.PROCESS_STATE_HOME;
+ return AppProtoEnums.PROCESS_STATE_HOME;
case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
- return ActivityManagerProto.PROCESS_STATE_LAST_ACTIVITY;
+ return AppProtoEnums.PROCESS_STATE_LAST_ACTIVITY;
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
- return ActivityManagerProto.PROCESS_STATE_CACHED_ACTIVITY;
+ return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY;
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
- return ActivityManagerProto.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+ return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
- return ActivityManagerProto.PROCESS_STATE_CACHED_RECENT;
+ return AppProtoEnums.PROCESS_STATE_CACHED_RECENT;
case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
- return ActivityManagerProto.PROCESS_STATE_CACHED_EMPTY;
+ return AppProtoEnums.PROCESS_STATE_CACHED_EMPTY;
case ActivityManager.PROCESS_STATE_NONEXISTENT:
- return ActivityManagerProto.PROCESS_STATE_NONEXISTENT;
+ return AppProtoEnums.PROCESS_STATE_NONEXISTENT;
+ case ActivityManager.PROCESS_STATE_UNKNOWN:
+ return AppProtoEnums.PROCESS_STATE_UNKNOWN;
default:
- return -1;
+ return AppProtoEnums.PROCESS_STATE_UNKNOWN_TO_PROTO;
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 979beed..57258a8 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -257,6 +257,29 @@
uid, iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort);
}
+ @Override
+ public synchronized void onTcpSocketStatsEvent(int[] networkIds,
+ int[] sentPackets, int[] lostPackets, int[] rttsUs, int[] sentAckDiffsMs) {
+ if (networkIds.length != sentPackets.length
+ || networkIds.length != lostPackets.length
+ || networkIds.length != rttsUs.length
+ || networkIds.length != sentAckDiffsMs.length) {
+ Log.e(TAG, "Mismatched lengths of TCP socket stats data arrays");
+ return;
+ }
+
+ long timestamp = System.currentTimeMillis();
+ for (int i = 0; i < networkIds.length; i++) {
+ int netId = networkIds[i];
+ int sent = sentPackets[i];
+ int lost = lostPackets[i];
+ int rttUs = rttsUs[i];
+ int sentAckDiffMs = sentAckDiffsMs[i];
+ getMetricsForNetwork(timestamp, netId)
+ .addTcpStatsResult(sent, lost, rttUs, sentAckDiffMs);
+ }
+ }
+
private void addWakeupEvent(WakeupEvent event) {
String iface = event.iface;
mWakeupEvents.append(event);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index ad2cf6c..422d0cb 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -20,6 +20,8 @@
import android.accounts.AccountAndUser;
import android.accounts.AccountManager;
import android.accounts.AccountManagerInternal;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.Notification;
@@ -32,6 +34,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.ISyncAdapter;
+import android.content.ISyncAdapterUnsyncableAccountCallback;
import android.content.ISyncContext;
import android.content.Intent;
import android.content.IntentFilter;
@@ -212,6 +215,10 @@
private static final int SYNC_OP_STATE_INVALID = 1;
private static final int SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS = 2;
+ /** Flags used when connecting to a sync adapter service */
+ private static final int SYNC_ADAPTER_CONNECTION_FLAGS = Context.BIND_AUTO_CREATE
+ | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT;
+
private Context mContext;
private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
@@ -876,7 +883,7 @@
public void scheduleSync(Account requestedAccount, int userId, int reason,
String requestedAuthority, Bundle extras, int targetSyncState) {
scheduleSync(requestedAccount, userId, reason, requestedAuthority, extras, targetSyncState,
- 0 /* min delay */);
+ 0 /* min delay */, true /* checkIfAccountReady */);
}
/**
@@ -884,7 +891,7 @@
*/
private void scheduleSync(Account requestedAccount, int userId, int reason,
String requestedAuthority, Bundle extras, int targetSyncState,
- final long minDelayMillis) {
+ final long minDelayMillis, boolean checkIfAccountReady) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (extras == null) {
extras = new Bundle();
@@ -963,7 +970,8 @@
}
for (String authority : syncableAuthorities) {
- int isSyncable = computeSyncable(account.account, account.userId, authority);
+ int isSyncable = computeSyncable(account.account, account.userId, authority,
+ !checkIfAccountReady);
if (isSyncable == AuthorityInfo.NOT_SYNCABLE) {
continue;
@@ -1000,7 +1008,8 @@
if (result != null
&& result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
scheduleSync(account.account, userId, reason, authority,
- finalExtras, targetSyncState, minDelayMillis);
+ finalExtras, targetSyncState, minDelayMillis,
+ true /* checkIfAccountReady */);
}
}
));
@@ -1009,7 +1018,7 @@
final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
- if (isSyncable < 0 && isAlwaysSyncable) {
+ if (!checkIfAccountReady && isSyncable < 0 && isAlwaysSyncable) {
mSyncStorageEngine.setIsSyncable(
account.account, account.userId, authority, AuthorityInfo.SYNCABLE);
isSyncable = AuthorityInfo.SYNCABLE;
@@ -1045,25 +1054,34 @@
final String owningPackage = syncAdapterInfo.componentName.getPackageName();
if (isSyncable == AuthorityInfo.NOT_INITIALIZED) {
- // Initialisation sync.
- Bundle newExtras = new Bundle();
- newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
- if (isLoggable) {
- Slog.v(TAG, "schedule initialisation Sync:"
- + ", delay until " + delayUntil
- + ", run by " + 0
- + ", flexMillis " + 0
- + ", source " + source
- + ", account " + account
- + ", authority " + authority
- + ", extras " + newExtras);
+ if (checkIfAccountReady) {
+ Bundle finalExtras = new Bundle(extras);
+
+ sendOnUnsyncableAccount(mContext, syncAdapterInfo, account.userId,
+ () -> scheduleSync(account.account, account.userId, reason,
+ authority, finalExtras, targetSyncState, minDelayMillis,
+ false));
+ } else {
+ // Initialisation sync.
+ Bundle newExtras = new Bundle();
+ newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
+ if (isLoggable) {
+ Slog.v(TAG, "schedule initialisation Sync:"
+ + ", delay until " + delayUntil
+ + ", run by " + 0
+ + ", flexMillis " + 0
+ + ", source " + source
+ + ", account " + account
+ + ", authority " + authority
+ + ", extras " + newExtras);
+ }
+ postScheduleSyncMessage(
+ new SyncOperation(account.account, account.userId,
+ owningUid, owningPackage, reason, source,
+ authority, newExtras, allowParallelSyncs),
+ minDelayMillis
+ );
}
- postScheduleSyncMessage(
- new SyncOperation(account.account, account.userId,
- owningUid, owningPackage, reason, source,
- authority, newExtras, allowParallelSyncs),
- minDelayMillis
- );
} else if (targetSyncState == AuthorityInfo.UNDEFINED
|| targetSyncState == isSyncable) {
if (isLoggable) {
@@ -1085,10 +1103,6 @@
}
}
- private int computeSyncable(Account account, int userId, String authority) {
- return computeSyncable(account, userId, authority, true);
- }
-
public int computeSyncable(Account account, int userId, String authority,
boolean checkAccountAccess) {
final int status = getIsSyncable(account, userId, authority);
@@ -1198,7 +1212,7 @@
final Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
scheduleSync(account, userId, reason, authority, extras,
- AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY);
+ AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY, true /* checkIfAccountReady */);
}
public SyncAdapterType[] getSyncAdapterTypes(int userId) {
@@ -1706,6 +1720,28 @@
}
/**
+ * Construct intent used to bind to an adapter.
+ *
+ * @param context Context to create intent for
+ * @param syncAdapterComponent The adapter description
+ * @param userId The user the adapter belongs to
+ *
+ * @return The intent required to bind to the adapter
+ */
+ static @NonNull Intent getAdapterBindIntent(@NonNull Context context,
+ @NonNull ComponentName syncAdapterComponent, @UserIdInt int userId) {
+ final Intent intent = new Intent();
+ intent.setAction("android.content.SyncAdapter");
+ intent.setComponent(syncAdapterComponent);
+ intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.sync_binding_label);
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(context, 0,
+ new Intent(Settings.ACTION_SYNC_SETTINGS), 0, null, UserHandle.of(userId)));
+
+ return intent;
+ }
+
+ /**
* @hide
*/
class ActiveSyncContext extends ISyncContext.Stub
@@ -1793,19 +1829,11 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this);
}
- Intent intent = new Intent();
- intent.setAction("android.content.SyncAdapter");
- intent.setComponent(serviceComponent);
- intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
- com.android.internal.R.string.sync_binding_label);
- intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
- mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
- null, new UserHandle(userId)));
+ Intent intent = getAdapterBindIntent(mContext, serviceComponent, userId);
+
mBound = true;
final boolean bindResult = mContext.bindServiceAsUser(intent, this,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_ALLOW_OOM_MANAGEMENT,
- new UserHandle(mSyncOperation.target.userId));
+ SYNC_ADAPTER_CONNECTION_FLAGS, new UserHandle(mSyncOperation.target.userId));
mLogger.log("bindService() returned=", mBound, " for ", this);
if (!bindResult) {
mBound = false;
@@ -2528,6 +2556,92 @@
}
}
+ interface OnReadyCallback {
+ void onReady();
+ }
+
+ static void sendOnUnsyncableAccount(@NonNull Context context,
+ @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo,
+ @UserIdInt int userId, @NonNull OnReadyCallback onReadyCallback) {
+ OnUnsyncableAccountCheck connection = new OnUnsyncableAccountCheck(syncAdapterInfo,
+ onReadyCallback);
+
+ boolean isBound = context.bindServiceAsUser(
+ getAdapterBindIntent(context, syncAdapterInfo.componentName, userId),
+ connection, SYNC_ADAPTER_CONNECTION_FLAGS, UserHandle.of(userId));
+
+ if (isBound) {
+ // Unbind after SERVICE_BOUND_TIME_MILLIS to not leak the connection.
+ (new Handler(Looper.getMainLooper())).postDelayed(
+ () -> context.unbindService(connection),
+ OnUnsyncableAccountCheck.SERVICE_BOUND_TIME_MILLIS);
+ } else {
+ /*
+ * The default implementation of adapter.onUnsyncableAccount returns true. Hence if
+ * there the service cannot be bound, assume the default behavior.
+ */
+ connection.onReady();
+ }
+ }
+
+
+ /**
+ * Helper class for calling ISyncAdapter.onUnsyncableAccountDone.
+ *
+ * If this returns {@code true} the onReadyCallback is called. Otherwise nothing happens.
+ */
+ private static class OnUnsyncableAccountCheck implements ServiceConnection {
+ static final long SERVICE_BOUND_TIME_MILLIS = 5000;
+
+ private final @NonNull OnReadyCallback mOnReadyCallback;
+ private final @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType>
+ mSyncAdapterInfo;
+
+ OnUnsyncableAccountCheck(
+ @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo,
+ @NonNull OnReadyCallback onReadyCallback) {
+ mSyncAdapterInfo = syncAdapterInfo;
+ mOnReadyCallback = onReadyCallback;
+ }
+
+ private void onReady() {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mOnReadyCallback.onReady();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final ISyncAdapter adapter = ISyncAdapter.Stub.asInterface(service);
+
+ try {
+ adapter.onUnsyncableAccount(new ISyncAdapterUnsyncableAccountCallback.Stub() {
+ @Override
+ public void onUnsyncableAccountDone(boolean isReady) {
+ if (isReady) {
+ onReady();
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not call onUnsyncableAccountDone " + mSyncAdapterInfo, e);
+ /*
+ * The default implementation of adapter.onUnsyncableAccount returns true. Hence if
+ * there is a crash in the implementation, assume the default behavior.
+ */
+ onReady();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // Wait until the service connects again
+ }
+ }
+
/**
* A helper object to keep track of the time we have spent syncing since the last boot
*/
@@ -3199,7 +3313,7 @@
return SYNC_OP_STATE_INVALID;
}
// Drop this sync request if it isn't syncable.
- state = computeSyncable(target.account, target.userId, target.provider);
+ state = computeSyncable(target.account, target.userId, target.provider, true);
if (state == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) {
if (isLoggable) {
Slog.v(TAG, " Dropping sync operation: "
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index e1e769c..db46c3d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -305,7 +305,7 @@
NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
InvalidKeyException, InvalidAlgorithmParameterException {
PlatformKeyManager platformKeyManager = mPlatformKeyManagerFactory.newInstance();
- PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey(mUserId);
+ PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey(mUserId);;
Map<String, WrappedKey> wrappedKeys = mRecoverableKeyStoreDb.getAllKeys(
mUserId, recoveryAgentUid, decryptKey.getGenerationId());
return WrappedKey.unwrapKeys(decryptKey, wrappedKeys);
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 7005de5..ee6a893 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -131,6 +131,7 @@
/**
* Generates a new key and increments the generation ID. Should be invoked if the platform key
* is corrupted and needs to be rotated.
+ * Updates status of old keys to {@code RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE}.
*
* @param userId The ID of the user to whose lock screen the platform key must be bound.
* @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
@@ -158,6 +159,7 @@
/**
* Returns the platform key used for encryption.
+ * Tries to regenerate key one time if it is permanently invalid.
*
* @param userId The ID of the user to whose lock screen the platform key must be bound.
* @throws KeyStoreException if there was an AndroidKeyStore error.
@@ -170,6 +172,30 @@
public PlatformEncryptionKey getEncryptKey(int userId) throws KeyStoreException,
UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
init(userId);
+ try {
+ return getEncryptKeyInternal(userId);
+ } catch (UnrecoverableKeyException e) {
+ Log.i(TAG, String.format(Locale.US,
+ "Regenerating permanently invalid Platform key for user %d.",
+ userId));
+ regenerate(userId);
+ return getEncryptKeyInternal(userId);
+ }
+ }
+
+ /**
+ * Returns the platform key used for encryption.
+ *
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
+ * @throws KeyStoreException if there was an AndroidKeyStore error.
+ * @throws UnrecoverableKeyException if the key could not be recovered.
+ * @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
+ * @throws InsecureUserException if the user does not have a lock screen set.
+ *
+ * @hide
+ */
+ private PlatformEncryptionKey getEncryptKeyInternal(int userId) throws KeyStoreException,
+ UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
int generationId = getGenerationId(userId);
AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
getEncryptAlias(userId, generationId), /*password=*/ null);
@@ -178,6 +204,32 @@
/**
* Returns the platform key used for decryption. Only works after a recent screen unlock.
+ * Tries to regenerate key one time if it is permanently invalid.
+ *
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
+ * @throws KeyStoreException if there was an AndroidKeyStore error.
+ * @throws UnrecoverableKeyException if the key could not be recovered.
+ * @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
+ * @throws InsecureUserException if the user does not have a lock screen set.
+ *
+ * @hide
+ */
+ public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException,
+ UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
+ init(userId);
+ try {
+ return getDecryptKeyInternal(userId);
+ } catch (UnrecoverableKeyException e) {
+ Log.i(TAG, String.format(Locale.US,
+ "Regenerating permanently invalid Platform key for user %d.",
+ userId));
+ regenerate(userId);
+ return getDecryptKeyInternal(userId);
+ }
+ }
+
+ /**
+ * Returns the platform key used for decryption. Only works after a recent screen unlock.
*
* @param userId The ID of the user to whose lock screen the platform key must be bound.
* @throws KeyStoreException if there was an AndroidKeyStore error.
@@ -187,9 +239,8 @@
*
* @hide
*/
- public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException,
+ private PlatformDecryptionKey getDecryptKeyInternal(int userId) throws KeyStoreException,
UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
- init(userId);
int generationId = getGenerationId(userId);
AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
getDecryptAlias(userId, generationId), /*password=*/ null);
@@ -294,13 +345,7 @@
String decryptAlias = getDecryptAlias(userId, generationId);
SecretKey secretKey = generateAesKey();
- mKeyStore.setEntry(
- encryptAlias,
- new KeyStore.SecretKeyEntry(secretKey),
- new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .build());
+ // Store Since decryption key first since it is more likely to fail.
mKeyStore.setEntry(
decryptAlias,
new KeyStore.SecretKeyEntry(secretKey),
@@ -313,6 +358,14 @@
.setBoundToSpecificSecureUserId(userId)
.build());
+ mKeyStore.setEntry(
+ encryptAlias,
+ new KeyStore.SecretKeyEntry(secretKey),
+ new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+
setGenerationId(userId, generationId);
try {
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 f2e71b3..b96208d 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
@@ -22,6 +22,7 @@
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.security.keystore.recovery.RecoveryController;
import android.text.TextUtils;
import android.util.Log;
@@ -289,8 +290,27 @@
ContentValues values = new ContentValues();
values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, userId);
values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID, generationId);
- return db.replace(
+ long result = db.replace(
UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
+ if (result != -1) {
+ invalidateKeysWithOldGenerationId(userId, generationId);
+ }
+ return result;
+ }
+
+ /**
+ * Updates status of old keys to {@code RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE}.
+ */
+ public void invalidateKeysWithOldGenerationId(int userId, int newGenerationId) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS,
+ RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+ String selection =
+ KeysEntry.COLUMN_NAME_USER_ID + " = ? AND "
+ + KeysEntry.COLUMN_NAME_GENERATION_ID + " < ?";
+ db.update(KeysEntry.TABLE_NAME, values, selection,
+ new String[] {String.valueOf(userId), String.valueOf(newGenerationId)});
}
/**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index cf36166..db83158 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3763,12 +3763,8 @@
proto.write(PowerManagerServiceDumpProto.UidStateProto.UID_STRING, UserHandle.formatUid(uid));
proto.write(PowerManagerServiceDumpProto.UidStateProto.IS_ACTIVE, state.mActive);
proto.write(PowerManagerServiceDumpProto.UidStateProto.NUM_WAKE_LOCKS, state.mNumWakeLocks);
- if (state.mProcState == ActivityManager.PROCESS_STATE_UNKNOWN) {
- proto.write(PowerManagerServiceDumpProto.UidStateProto.IS_PROCESS_STATE_UNKNOWN, true);
- } else {
- proto.write(PowerManagerServiceDumpProto.UidStateProto.PROCESS_STATE,
- ActivityManager.processStateAmToProto(state.mProcState));
- }
+ proto.write(PowerManagerServiceDumpProto.UidStateProto.PROCESS_STATE,
+ ActivityManager.processStateAmToProto(state.mProcState));
proto.end(uIDToken);
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdater.java b/services/core/java/com/android/server/webkit/WebViewUpdater.java
index 7e05e46..61a3c09 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdater.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdater.java
@@ -507,22 +507,16 @@
if (systemInterface.systemIsDebuggable()) {
return true;
}
- Signature[] packageSignatures;
// If no signature is declared, instead check whether the package is included in the
// system.
if (provider.signatures == null || provider.signatures.length == 0) {
return packageInfo.applicationInfo.isSystemApp();
}
- packageSignatures = packageInfo.signatures;
- if (packageSignatures.length != 1)
- return false;
+ if (packageInfo.signatures.length != 1) return false;
- final byte[] packageSignature = packageSignatures[0].toByteArray();
// Return whether the package signature matches any of the valid signatures
- for (String signature : provider.signatures) {
- final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
- if (Arrays.equals(packageSignature, validSignature))
- return true;
+ for (Signature signature : provider.signatures) {
+ if (signature.equals(packageInfo.signatures[0])) return true;
}
return false;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index f79719c..212a0d70 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -223,6 +223,27 @@
return null;
}
+ if (top.hasCommittedReparentToAnimationLeash()) {
+ if (DEBUG_SCREENSHOT) {
+ Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + top);
+ }
+ return null;
+ }
+
+ final boolean hasVisibleChild = top.forAllWindows(
+ // Ensure at least one window for the top app is visible before attempting to take
+ // a screenshot. Visible here means that the WSA surface is shown and has an alpha
+ // greater than 0.
+ ws -> ws.mWinAnimator != null && ws.mWinAnimator.getShown()
+ && ws.mWinAnimator.mLastAlpha > 0f, true);
+
+ if (!hasVisibleChild) {
+ if (DEBUG_SCREENSHOT) {
+ Slog.w(TAG_WM, "Failed to take screenshot. No visible windows for " + task);
+ }
+ return null;
+ }
+
final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
final float scaleFraction = isLowRamDevice ? REDUCED_SCALE : 1f;
task.getBounds(mTmpRect);
@@ -233,7 +254,7 @@
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
if (DEBUG_SCREENSHOT) {
- Slog.w(TAG_WM, "Failed to take screenshot");
+ Slog.w(TAG_WM, "Failed to take screenshot for " + task);
}
return null;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 42c6ec2..8a500b5 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -100,6 +100,12 @@
/** Total number of elements in this subtree, including our own hierarchy element. */
private int mTreeWeight = 1;
+ /**
+ * Indicates whether we are animating and have committed the transaction to reparent our
+ * surface to the animation leash
+ */
+ private boolean mCommittedReparentToAnimationLeash;
+
WindowContainer(WindowManagerService service) {
mService = service;
mPendingTransaction = service.mTransactionFactory.make();
@@ -1025,12 +1031,24 @@
*/
void prepareSurfaces() {
SurfaceControl.mergeToGlobalTransaction(getPendingTransaction());
+
+ // If a leash has been set when the transaction was committed, then the leash reparent has
+ // been committed.
+ mCommittedReparentToAnimationLeash = mSurfaceAnimator.hasLeash();
for (int i = 0; i < mChildren.size(); i++) {
mChildren.get(i).prepareSurfaces();
}
}
/**
+ * @return true if the reparent to animation leash transaction has been committed, false
+ * otherwise.
+ */
+ boolean hasCommittedReparentToAnimationLeash() {
+ return mCommittedReparentToAnimationLeash;
+ }
+
+ /**
* Trigger a call to prepareSurfaces from the animation thread, such that
* mPendingTransaction will be applied.
*/
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index b1bff70..6fc9e08 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -23,6 +23,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -48,6 +49,7 @@
import java.io.File;
import java.security.KeyStore;
+import java.security.UnrecoverableKeyException;
import java.util.List;
@SmallTest
@@ -258,6 +260,40 @@
}
@Test
+ public void getEncryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception {
+ doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+ any());
+
+ mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
+
+ verify(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+ any());
+ // Attempt to get regenerated key.
+ verify(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
+ any());
+ }
+
+ @Test
+ public void getDecryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception {
+ doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ any());
+
+ mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
+
+ verify(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ any());
+ // Attempt to get regenerated key.
+ verify(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ any());
+ }
+
+ @Test
public void getEncryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
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 f0254c6..097d214 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
@@ -315,6 +315,29 @@
assertThat(statuses).hasSize(0);
}
+ public void testInvalidateKeysWithOldGenerationId_withSingleKey() {
+ int userId = 12;
+ int uid = 1009;
+ int generationId = 6;
+ int status = 120;
+ int status2 = 121;
+ String alias = "test";
+ byte[] nonce = getUtf8Bytes("nonce");
+ byte[] keyMaterial = getUtf8Bytes("keymaterial");
+ WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+ mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
+
+ WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
+ assertThat(retrievedKey.getRecoveryStatus()).isEqualTo(status);
+
+ mRecoverableKeyStoreDb.setRecoveryStatus(uid, alias, status2);
+ mRecoverableKeyStoreDb.invalidateKeysWithOldGenerationId(userId, generationId + 1);
+
+ retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
+ assertThat(retrievedKey.getRecoveryStatus())
+ .isEqualTo(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+ }
+
@Test
public void setRecoveryServicePublicKey_replaceOldKey() throws Exception {
int userId = 12;
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 88bae33..a1a6a5a 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -110,6 +110,9 @@
/** Result code of execution with no error. */
public static final int RESULT_OK = 0;
+ /** Result code of an unknown error. */
+ public static final int RESULT_UNKNOWN_ERROR = -1;
+
/**
* Callback to receive the result of an eUICC card API.
*