Merge "Improved trust error messaging (1/2)"
diff --git a/Android.mk b/Android.mk
index 6851765..53e892f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -442,6 +442,7 @@
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp core-junit bouncycastle ext
+LOCAL_STATIC_JAVA_LIBRARIES := framework-protos
LOCAL_MODULE := framework
diff --git a/api/current.txt b/api/current.txt
index ed04a6f..2d19b9a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3676,9 +3676,6 @@
method public void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
}
- public static abstract class ActivityManager.BugreportMode implements java.lang.annotation.Annotation {
- }
-
public static class ActivityManager.MemoryInfo implements android.os.Parcelable {
ctor public ActivityManager.MemoryInfo();
method public int describeContents();
@@ -5783,9 +5780,6 @@
field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
}
- public static abstract class DeviceAdminReceiver.BugreportFailureCode implements java.lang.annotation.Annotation {
- }
-
public class DevicePolicyManager {
method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
@@ -34412,6 +34406,8 @@
public abstract class UtteranceProgressListener {
ctor public UtteranceProgressListener();
+ method public void onAudioAvailable(java.lang.String, byte[]);
+ method public void onBeginSynthesis(java.lang.String, int, int, int);
method public abstract void onDone(java.lang.String);
method public abstract deprecated void onError(java.lang.String);
method public void onError(java.lang.String, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 825fe75..733ad28 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3798,9 +3798,6 @@
method public void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
}
- public static abstract class ActivityManager.BugreportMode implements java.lang.annotation.Annotation {
- }
-
public static class ActivityManager.MemoryInfo implements android.os.Parcelable {
ctor public ActivityManager.MemoryInfo();
method public int describeContents();
@@ -5918,9 +5915,6 @@
field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
}
- public static abstract class DeviceAdminReceiver.BugreportFailureCode implements java.lang.annotation.Annotation {
- }
-
public class DevicePolicyManager {
method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
@@ -36662,6 +36656,8 @@
public abstract class UtteranceProgressListener {
ctor public UtteranceProgressListener();
+ method public void onAudioAvailable(java.lang.String, byte[]);
+ method public void onBeginSynthesis(java.lang.String, int, int, int);
method public abstract void onDone(java.lang.String);
method public abstract deprecated void onError(java.lang.String);
method public void onError(java.lang.String, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index f096d56..e9173741 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3676,9 +3676,6 @@
method public void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
}
- public static abstract class ActivityManager.BugreportMode implements java.lang.annotation.Annotation {
- }
-
public static class ActivityManager.MemoryInfo implements android.os.Parcelable {
ctor public ActivityManager.MemoryInfo();
method public int describeContents();
@@ -5785,9 +5782,6 @@
field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
}
- public static abstract class DeviceAdminReceiver.BugreportFailureCode implements java.lang.annotation.Annotation {
- }
-
public class DevicePolicyManager {
method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
@@ -34426,6 +34420,8 @@
public abstract class UtteranceProgressListener {
ctor public UtteranceProgressListener();
+ method public void onAudioAvailable(java.lang.String, byte[]);
+ method public void onBeginSynthesis(java.lang.String, int, int, int);
method public abstract void onDone(java.lang.String);
method public abstract deprecated void onError(java.lang.String);
method public void onError(java.lang.String, int);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2dc4fb9..6307477 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -85,16 +85,16 @@
private final Context mContext;
private final Handler mHandler;
+ /**
+ * Defines acceptable types of bugreports.
+ * @hide
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
BUGREPORT_OPTION_FULL,
BUGREPORT_OPTION_INTERACTIVE,
BUGREPORT_OPTION_REMOTE
})
- /**
- * Defines acceptable types of bugreports.
- * @hide
- */
public @interface BugreportMode {}
/**
* Takes a bugreport without user interference (and hence causing less
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index cfd5ca8..3c1ecc7 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -283,17 +283,17 @@
public static final String EXTRA_BUGREPORT_FAILURE_REASON =
"android.app.extra.BUGREPORT_FAILURE_REASON";
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- BUGREPORT_FAILURE_FAILED_COMPLETING,
- BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE
- })
/**
* An interface representing reason of bugreport failure.
*
* @see #EXTRA_BUGREPORT_FAILURE_REASON
* @hide
*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ BUGREPORT_FAILURE_FAILED_COMPLETING,
+ BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE
+ })
public @interface BugreportFailureCode {}
/** Bugreport completion process failed. */
public static final int BUGREPORT_FAILURE_FAILED_COMPLETING = 0;
diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java
index dea766b..ca9931a 100644
--- a/core/java/android/speech/tts/FileSynthesisCallback.java
+++ b/core/java/android/speech/tts/FileSynthesisCallback.java
@@ -111,6 +111,7 @@
"of AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT or " +
"AudioFormat.ENCODING_PCM_FLOAT");
}
+ mDispatcher.dispatchOnBeginSynthesis(sampleRateInHz, audioFormat, channelCount);
FileChannel fileChannel = null;
synchronized (mStateLock) {
@@ -176,6 +177,10 @@
fileChannel = mFileChannel;
}
+ final byte[] bufferCopy = new byte[length];
+ System.arraycopy(buffer, offset, bufferCopy, 0, length);
+ mDispatcher.dispatchOnAudioAvailable(bufferCopy);
+
try {
fileChannel.write(ByteBuffer.wrap(buffer, offset, length));
return TextToSpeech.SUCCESS;
diff --git a/core/java/android/speech/tts/ITextToSpeechCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
index d785c3f..4e3acf6 100644
--- a/core/java/android/speech/tts/ITextToSpeechCallback.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
@@ -22,33 +22,65 @@
*/
oneway interface ITextToSpeechCallback {
/**
- * Tells the client that the synthesis has started.
+ * Tells the client that the synthesis has started playing.
*
- * @param utteranceId Unique id identifying synthesis request.
+ * @param utteranceId Unique id identifying the synthesis request.
*/
void onStart(String utteranceId);
/**
- * Tells the client that the synthesis has finished.
+ * Tells the client that the synthesis has finished playing.
*
- * @param utteranceId Unique id identifying synthesis request.
+ * @param utteranceId Unique id identifying the synthesis request.
*/
void onSuccess(String utteranceId);
/**
* Tells the client that the synthesis was stopped.
*
- * @param utteranceId Unique id identifying synthesis request.
+ * @param utteranceId Unique id identifying the synthesis request.
*/
void onStop(String utteranceId, boolean isStarted);
/**
* Tells the client that the synthesis has failed.
*
- * @param utteranceId Unique id identifying synthesis request.
+ * @param utteranceId Unique id identifying the synthesis request.
* @param errorCode One of the values from
* {@link android.speech.tts.v2.TextToSpeech}.
*/
void onError(String utteranceId, int errorCode);
+ /**
+ * Tells the client that the TTS engine has started synthesizing the audio for a request.
+ *
+ * <p>
+ * This doesn't mean the synthesis request has already started playing (for example when there
+ * are synthesis requests ahead of it in the queue), but after receiving this callback you can
+ * expect onAudioAvailable to be called.
+ * </p>
+ *
+ * @param utteranceId Unique id identifying the synthesis request.
+ * @param sampleRateInHz Sample rate in HZ of the generated audio.
+ * @param audioFormat The audio format of the generated audio in the {@link #onAudioAvailable}
+ * call. Should be one of {@link android.media.AudioFormat.ENCODING_PCM_8BIT},
+ * {@link android.media.AudioFormat.ENCODING_PCM_16BIT} or
+ * {@link android.media.AudioFormat.ENCODING_PCM_FLOAT}.
+ * @param channelCount The number of channels.
+ */
+ void onBeginSynthesis(String utteranceId, int sampleRateInHz, int audioFormat, int channelCount);
+
+ /**
+ * Tells the client about a chunk of the synthesized audio.
+ *
+ * <p>
+ * Called when a chunk of the synthesized audio is ready. This may be called more than once for
+ * every synthesis request, thereby streaming the audio to the client.
+ * </p>
+ *
+ * @param utteranceId Unique id identifying the synthesis request.
+ * @param audio The raw audio bytes. Its format is specified by the {@link #onStartAudio}
+ * callback.
+ */
+ void onAudioAvailable(String utteranceId, in byte[] audio);
}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index dcc0095..778aa86 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -15,6 +15,7 @@
*/
package android.speech.tts;
+import android.annotation.NonNull;
import android.media.AudioFormat;
import android.speech.tts.TextToSpeechService.AudioOutputParams;
import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
@@ -51,9 +52,10 @@
private final Object mCallerIdentity;
private final AbstractEventLogger mLogger;
- PlaybackSynthesisCallback(AudioOutputParams audioParams, AudioPlaybackHandler audioTrackHandler,
- UtteranceProgressDispatcher dispatcher, Object callerIdentity,
- AbstractEventLogger logger, boolean clientIsUsingV2) {
+ PlaybackSynthesisCallback(@NonNull AudioOutputParams audioParams,
+ @NonNull AudioPlaybackHandler audioTrackHandler,
+ @NonNull UtteranceProgressDispatcher dispatcher, @NonNull Object callerIdentity,
+ @NonNull AbstractEventLogger logger, boolean clientIsUsingV2) {
super(clientIsUsingV2);
mAudioParams = audioParams;
mAudioTrackHandler = audioTrackHandler;
@@ -130,6 +132,7 @@
"of AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT or " +
"AudioFormat.ENCODING_PCM_FLOAT");
}
+ mDispatcher.dispatchOnBeginSynthesis(sampleRateInHz, audioFormat, channelCount);
int channelConfig = BlockingAudioTrack.getChannelConfig(channelCount);
@@ -190,6 +193,7 @@
// Sigh, another copy.
final byte[] bufferCopy = new byte[length];
System.arraycopy(buffer, offset, bufferCopy, 0, length);
+ mDispatcher.dispatchOnAudioAvailable(bufferCopy);
// Might block on mItem.this, if there are too many buffers waiting to
// be consumed.
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 61c33ff..d55c7bd 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -16,6 +16,7 @@
package android.speech.tts;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.RawRes;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -665,7 +666,7 @@
private OnInitListener mInitListener;
// Written from an unspecified application thread, read from
// a binder thread.
- private volatile UtteranceProgressListener mUtteranceProgressListener;
+ @Nullable private volatile UtteranceProgressListener mUtteranceProgressListener;
private final Object mStartLock = new Object();
private String mRequestedEngine;
@@ -2133,6 +2134,23 @@
listener.onStart(utteranceId);
}
}
+
+ @Override
+ public void onBeginSynthesis(String utteranceId, int sampleRateInHz, int audioFormat,
+ int channelCount) {
+ UtteranceProgressListener listener = mUtteranceProgressListener;
+ if (listener != null) {
+ listener.onBeginSynthesis(utteranceId, sampleRateInHz, audioFormat, channelCount);
+ }
+ }
+
+ @Override
+ public void onAudioAvailable(String utteranceId, byte[] audio) {
+ UtteranceProgressListener listener = mUtteranceProgressListener;
+ if (listener != null) {
+ listener.onAudioAvailable(utteranceId, audio);
+ }
+ }
};
private class SetupConnectionAsyncTask extends AsyncTask<Void, Void, Integer> {
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 8c355d8..fc075de 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -15,6 +15,7 @@
*/
package android.speech.tts;
+import android.annotation.NonNull;
import android.app.Service;
import android.content.Intent;
import android.media.AudioAttributes;
@@ -111,7 +112,7 @@
// A thread and it's associated handler for playing back any audio
// associated with this TTS engine. Will handle all requests except synthesis
// to file requests, which occur on the synthesis thread.
- private AudioPlaybackHandler mAudioPlaybackHandler;
+ @NonNull private AudioPlaybackHandler mAudioPlaybackHandler;
private TtsEngines mEngineHelper;
private CallbackMap mCallbacks;
@@ -649,6 +650,8 @@
public void dispatchOnSuccess();
public void dispatchOnStart();
public void dispatchOnError(int errorCode);
+ public void dispatchOnBeginSynthesis(int sampleRateInHz, int audioFormat, int channelCount);
+ public void dispatchOnAudioAvailable(byte[] audio);
}
/** Set of parameters affecting audio output. */
@@ -853,6 +856,22 @@
}
}
+ @Override
+ public void dispatchOnBeginSynthesis(int sampleRateInHz, int audioFormat, int channelCount) {
+ final String utteranceId = getUtteranceId();
+ if (utteranceId != null) {
+ mCallbacks.dispatchOnBeginSynthesis(getCallerIdentity(), utteranceId, sampleRateInHz, audioFormat, channelCount);
+ }
+ }
+
+ @Override
+ public void dispatchOnAudioAvailable(byte[] audio) {
+ final String utteranceId = getUtteranceId();
+ if (utteranceId != null) {
+ mCallbacks.dispatchOnAudioAvailable(getCallerIdentity(), utteranceId, audio);
+ }
+ }
+
abstract public String getUtteranceId();
String getStringParam(Bundle params, String key, String defaultValue) {
@@ -1430,7 +1449,6 @@
} catch (RemoteException e) {
Log.e(TAG, "Callback onStart failed: " + e);
}
-
}
public void dispatchOnError(Object callerIdentity, String utteranceId,
@@ -1444,6 +1462,26 @@
}
}
+ public void dispatchOnBeginSynthesis(Object callerIdentity, String utteranceId, int sampleRateInHz, int audioFormat, int channelCount) {
+ ITextToSpeechCallback cb = getCallbackFor(callerIdentity);
+ if (cb == null) return;
+ try {
+ cb.onBeginSynthesis(utteranceId, sampleRateInHz, audioFormat, channelCount);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback dispatchOnBeginSynthesis(String, int, int, int) failed: " + e);
+ }
+ }
+
+ public void dispatchOnAudioAvailable(Object callerIdentity, String utteranceId, byte[] buffer) {
+ ITextToSpeechCallback cb = getCallbackFor(callerIdentity);
+ if (cb == null) return;
+ try {
+ cb.onAudioAvailable(utteranceId, buffer);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback dispatchOnAudioAvailable(String, byte[]) failed: " + e);
+ }
+ }
+
@Override
public void onCallbackDied(ITextToSpeechCallback callback, Object cookie) {
IBinder caller = (IBinder) cookie;
diff --git a/core/java/android/speech/tts/UtteranceProgressListener.java b/core/java/android/speech/tts/UtteranceProgressListener.java
index 890ea3d..72a5228 100644
--- a/core/java/android/speech/tts/UtteranceProgressListener.java
+++ b/core/java/android/speech/tts/UtteranceProgressListener.java
@@ -2,6 +2,8 @@
package android.speech.tts;
+import android.media.AudioFormat;
+
/**
* Listener for events relating to the progress of an utterance through
* the synthesis queue. Each utterance is associated with a call to
@@ -14,10 +16,10 @@
/**
* Called when an utterance "starts" as perceived by the caller. This will
* be soon before audio is played back in the case of a {@link TextToSpeech#speak}
- * or before the first bytes of a file are written to storage in the case
+ * or before the first bytes of a file are written to the file system in the case
* of {@link TextToSpeech#synthesizeToFile}.
*
- * @param utteranceId the utterance ID of the utterance.
+ * @param utteranceId The utterance ID of the utterance.
*/
public abstract void onStart(String utteranceId);
@@ -28,7 +30,7 @@
*
* This request is guaranteed to be called after {@link #onStart(String)}.
*
- * @param utteranceId the utterance ID of the utterance.
+ * @param utteranceId The utterance ID of the utterance.
*/
public abstract void onDone(String utteranceId);
@@ -39,7 +41,7 @@
* be a call to both {@link #onDone(String)} and {@link #onError(String)} for
* the same utterance.
*
- * @param utteranceId the utterance ID of the utterance.
+ * @param utteranceId The utterance ID of the utterance.
* @deprecated Use {@link #onError(String,int)} instead
*/
@Deprecated
@@ -52,7 +54,7 @@
* be a call to both {@link #onDone(String)} and {@link #onError(String,int)} for
* the same utterance. The default implementation calls {@link #onError(String)}.
*
- * @param utteranceId the utterance ID of the utterance.
+ * @param utteranceId The utterance ID of the utterance.
* @param errorCode one of the ERROR_* codes from {@link TextToSpeech}
*/
public void onError(String utteranceId, int errorCode) {
@@ -65,7 +67,7 @@
* or uses {@link TextToSpeech#QUEUE_FLUSH} as an argument with the
* {@link TextToSpeech#speak} or {@link TextToSpeech#synthesizeToFile} methods.
*
- * @param utteranceId the utterance ID of the utterance.
+ * @param utteranceId The utterance ID of the utterance.
* @param interrupted If true, then the utterance was interrupted while being synthesized
* and its output is incomplete. If false, then the utterance was flushed
* before the synthesis started.
@@ -74,6 +76,52 @@
}
/**
+ * Called when the TTS engine begins to synthesize the audio for a request.
+ *
+ * <p>
+ * It provides information about the format of the byte array for subsequent
+ * {@link #onAudioAvailable} calls.
+ * </p>
+ *
+ * <p>
+ * This is called when the TTS engine starts synthesizing audio for the request. If an
+ * application wishes to know when the audio is about to start playing, {#onStart(String)}
+ * should be used instead.
+ * </p>
+ *
+ * @param utteranceId The utterance ID of the utterance.
+ * @param sampleRateInHz Sample rate in hertz of the generated audio.
+ * @param audioFormat Audio format of the generated audio. Should be one of
+ * {@link AudioFormat#ENCODING_PCM_8BIT}, {@link AudioFormat#ENCODING_PCM_16BIT} or
+ * {@link AudioFormat#ENCODING_PCM_FLOAT}.
+ * @param channelCount The number of channels.
+ */
+ public void onBeginSynthesis(String utteranceId, int sampleRateInHz, int audioFormat, int channelCount) {
+ }
+
+ /**
+ * This is called when a chunk of audio is ready for consumption.
+ *
+ * <p>
+ * The audio parameter is a copy of what will be synthesized to the speakers (when synthesis was
+ * initiated with a {@link TextToSpeech#speak} call) or written to the file system (for
+ * {@link TextToSpeech#synthesizeToFile}). The audio bytes are delivered in one or more chunks;
+ * if {@link #onDone} or {@link #onError} is called all chunks have been received.
+ * </p>
+ *
+ * <p>
+ * The audio received here may not be played for some time depending on buffer sizes and the
+ * amount of items on the synthesis queue.
+ * </p>
+ *
+ * @param utteranceId The utterance ID of the utterance.
+ * @param audio A chunk of audio; the format can be known by listening to
+ * {@link #onBeginSynthesis(String, int, int, int)}.
+ */
+ public void onAudioAvailable(String utteranceId, byte[] audio) {
+ }
+
+ /**
* Wraps an old deprecated OnUtteranceCompletedListener with a shiny new
* progress listener.
*
diff --git a/core/java/android/view/FrameStatsObserver.java b/core/java/android/view/FrameStatsObserver.java
new file mode 100644
index 0000000..0add607
--- /dev/null
+++ b/core/java/android/view/FrameStatsObserver.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.util.Log;
+import android.os.Looper;
+import android.os.MessageQueue;
+
+import com.android.internal.util.VirtualRefBasePtr;
+
+import java.lang.NullPointerException;
+import java.lang.ref.WeakReference;
+import java.lang.SuppressWarnings;
+
+/**
+ * Provides streaming access to frame stats information from the rendering
+ * subsystem to apps.
+ *
+ * @hide
+ */
+public abstract class FrameStatsObserver {
+ private static final String TAG = "FrameStatsObserver";
+
+ private MessageQueue mMessageQueue;
+ private long[] mBuffer;
+
+ private FrameStats mFrameStats;
+
+ /* package */ ThreadedRenderer mRenderer;
+ /* package */ VirtualRefBasePtr mNative;
+
+ /**
+ * Containing class for frame statistics reported
+ * by the rendering subsystem.
+ */
+ public static class FrameStats {
+ /**
+ * Precise timing data for various milestones in a frame
+ * lifecycle.
+ *
+ * This data is exactly the same as what is returned by
+ * `adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats`
+ *
+ * The fields reported may change from release to release.
+ *
+ * @see {@link http://developer.android.com/training/testing/performance.html}
+ * for a description of the fields present.
+ */
+ public long[] mTimingData;
+ }
+
+ /**
+ * Creates a FrameStatsObserver
+ *
+ * @param looper the looper to use when invoking callbacks
+ */
+ public FrameStatsObserver(@NonNull Looper looper) {
+ if (looper == null) {
+ throw new NullPointerException("looper cannot be null");
+ }
+
+ mMessageQueue = looper.getQueue();
+ if (mMessageQueue == null) {
+ throw new IllegalStateException("invalid looper, null message queue\n");
+ }
+
+ mFrameStats = new FrameStats();
+ }
+
+ /**
+ * Called on provided looper when frame stats data is available
+ * for the previous frame.
+ *
+ * Clients of this class must do as little work as possible within
+ * this callback, as the buffer is shared between the producer and consumer.
+ *
+ * If the consumer is still executing within this method when there is new
+ * data available that data will be dropped. The producer cannot
+ * wait on the consumer.
+ *
+ * @param data the newly available data
+ */
+ public abstract void onDataAvailable(FrameStats data);
+
+ /**
+ * Returns the number of reports dropped as a result of a slow
+ * consumer.
+ */
+ public long getDroppedReportCount() {
+ if (mRenderer == null) {
+ return 0;
+ }
+
+ return mRenderer.getDroppedFrameReportCount();
+ }
+
+ public boolean isRegistered() {
+ return mRenderer != null && mNative != null;
+ }
+
+ // === called by native === //
+ @SuppressWarnings("unused")
+ private void notifyDataAvailable() {
+ mFrameStats.mTimingData = mBuffer;
+ onDataAvailable(mFrameStats);
+ }
+}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 0e4bc84..78a63a6 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -24,7 +24,9 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -34,12 +36,14 @@
import android.view.View.AttachInfo;
import com.android.internal.R;
+import com.android.internal.util.VirtualRefBasePtr;
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
/**
* Hardware renderer that proxies the rendering to a render thread. Most calls
@@ -339,6 +343,8 @@
private boolean mEnabled;
private boolean mRequested = true;
+ private HashSet<FrameStatsObserver> mFrameStatsObservers;
+
ThreadedRenderer(Context context, boolean translucent) {
final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
@@ -947,6 +953,31 @@
}
}
+ void addFrameStatsObserver(FrameStatsObserver fso) {
+ if (mFrameStatsObservers == null) {
+ mFrameStatsObservers = new HashSet<>();
+ }
+
+ long nativeFso = nAddFrameStatsObserver(mNativeProxy, fso);
+ fso.mRenderer = this;
+ fso.mNative = new VirtualRefBasePtr(nativeFso);
+ mFrameStatsObservers.add(fso);
+ }
+
+ void removeFrameStatsObserver(FrameStatsObserver fso) {
+ if (!mFrameStatsObservers.remove(fso)) {
+ throw new IllegalArgumentException("attempt to remove FrameStatsObserver that was never added");
+ }
+
+ nRemoveFrameStatsObserver(mNativeProxy, fso.mNative.get());
+ fso.mRenderer = null;
+ fso.mNative = null;
+ }
+
+ long getDroppedFrameReportCount() {
+ return nGetDroppedFrameReportCount(mNativeProxy);
+ }
+
static native void setupShadersDiskCache(String cacheFile);
private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
@@ -1000,4 +1031,8 @@
private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
private static native void nSetContentDrawBounds(long nativeProxy, int left,
int top, int right, int bottom);
+
+ private static native long nAddFrameStatsObserver(long nativeProxy, FrameStatsObserver fso);
+ private static native void nRemoveFrameStatsObserver(long nativeProxy, long nativeFso);
+ private static native long nGetDroppedFrameReportCount(long nativeProxy);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fff141a..6c3f308 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -110,6 +110,7 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import java.lang.NullPointerException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
@@ -3702,6 +3703,11 @@
private ViewPropertyAnimator mAnimator = null;
/**
+ * List of FrameStatsObservers pending registration when mAttachInfo is null.
+ */
+ private ArrayList<FrameStatsObserver> mPendingFrameStatsObservers;
+
+ /**
* Flag indicating that a drag can cross window boundaries. When
* {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called
* with this flag set, all visible applications will be able to participate
@@ -5381,6 +5387,58 @@
}
/**
+ * Set an observer to collect stats for each frame rendered for this view.
+ *
+ * @hide
+ */
+ public void addFrameStatsObserver(FrameStatsObserver fso) {
+ if (mAttachInfo != null) {
+ if (mAttachInfo.mHardwareRenderer != null) {
+ mAttachInfo.mHardwareRenderer.addFrameStatsObserver(fso);
+ } else {
+ Log.w(VIEW_LOG_TAG, "View not hardware-accelerated. Unable to observe frame stats");
+ }
+ } else {
+ if (mPendingFrameStatsObservers == null) {
+ mPendingFrameStatsObservers = new ArrayList<>();
+ }
+
+ mPendingFrameStatsObservers.add(fso);
+ }
+ }
+
+ /**
+ * Remove observer configured to collect frame stats for this view.
+ *
+ * @hide
+ */
+ public void removeFrameStatsObserver(FrameStatsObserver fso) {
+ ThreadedRenderer renderer = getHardwareRenderer();
+
+ if (mPendingFrameStatsObservers != null) {
+ mPendingFrameStatsObservers.remove(fso);
+ }
+
+ if (renderer != null) {
+ renderer.removeFrameStatsObserver(fso);
+ }
+ }
+
+ private void registerPendingFrameStatsObservers() {
+ if (mPendingFrameStatsObservers != null) {
+ ThreadedRenderer renderer = getHardwareRenderer();
+ if (renderer != null) {
+ for (FrameStatsObserver fso : mPendingFrameStatsObservers) {
+ renderer.addFrameStatsObserver(fso);
+ }
+ } else {
+ Log.w(VIEW_LOG_TAG, "View not hardware-accelerated. Unable to observe frame stats");
+ }
+ mPendingFrameStatsObservers = null;
+ }
+ }
+
+ /**
* Call this view's OnClickListener, if it is defined. Performs all normal
* actions associated with clicking: reporting accessibility event, playing
* a sound, etc.
@@ -14903,6 +14961,9 @@
info.mTreeObserver.merge(mFloatingTreeObserver);
mFloatingTreeObserver = null;
}
+
+ registerPendingFrameStatsObservers();
+
if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {
mAttachInfo.mScrollContainers.add(this);
mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index dfe0cc7..ee70891 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -34,6 +34,7 @@
import android.media.session.MediaController;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -794,6 +795,40 @@
return mCallback;
}
+ /**
+ * Set an observer to collect frame stats for each frame rendererd in this window.
+ *
+ * Must be in hardware rendering mode.
+ * @hide
+ */
+ public final void addFrameStatsObserver(@NonNull FrameStatsObserver fso) {
+ final View decorView = getDecorView();
+ if (decorView == null) {
+ throw new IllegalStateException("can't observe a Window without an attached view");
+ }
+
+ if (fso == null) {
+ throw new NullPointerException("FrameStatsObserver cannot be null");
+ }
+
+ if (fso.isRegistered()) {
+ throw new IllegalStateException("FrameStatsObserver already registered on a Window.");
+ }
+
+ decorView.addFrameStatsObserver(fso);
+ }
+
+ /**
+ * Remove observer and stop listening to frame stats for this window.
+ * @hide
+ */
+ public final void removeFrameStatsObserver(FrameStatsObserver fso) {
+ final View decorView = getDecorView();
+ if (decorView != null) {
+ getDecorView().removeFrameStatsObserver(fso);
+ }
+ }
+
/** @hide */
public final void setOnWindowDismissedCallback(OnWindowDismissedCallback dcb) {
mOnWindowDismissedCallback = dcb;
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
index 37bf71c..9884585 100644
--- a/core/java/com/android/internal/logging/MetricsConstants.java
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -22,8 +22,7 @@
*/
public interface MetricsConstants {
// These constants must match those in the analytic pipeline, do not edit.
- // Add temporary values to the top of MetricsLogger instead.
- public static final int VIEW_UNKNOWN = 0;
+ // define metric categories in frameworks/base/core/proto/src/metrics_constants.proto.
public static final int MAIN_SETTINGS = 1;
public static final int ACCESSIBILITY = 2;
public static final int ACCESSIBILITY_CAPTION_PROPERTIES = 3;
@@ -65,7 +64,7 @@
public static final int DEVELOPMENT = 39;
public static final int DEVICEINFO = 40;
public static final int DEVICEINFO_IMEI_INFORMATION = 41;
- public static final int DEVICEINFO_MEMORY = 42;
+ public static final int DEVICEINFO_STORAGE = 42;
public static final int DEVICEINFO_SIM_STATUS = 43;
public static final int DEVICEINFO_STATUS = 44;
public static final int DEVICEINFO_USB = 45;
@@ -134,7 +133,6 @@
public static final int WIFI_INFO = 108;
public static final int WIFI_P2P = 109;
public static final int WIRELESS = 110;
- public static final int QS_PANEL = 111;
public static final int QS_AIRPLANEMODE = 112;
public static final int QS_BLUETOOTH = 113;
public static final int QS_CAST = 114;
@@ -282,10 +280,55 @@
public static final int ACTION_WIGGLE_CAMERA_GESTURE = 256;
public static final int QS_WORKMODE = 257;
public static final int BACKGROUND_CHECK_SUMMARY = 258;
+ public static final int QS_LOCK_TILE = 259;
+ public static final int QS_USER_TILE = 260;
+ public static final int QS_BATTERY_TILE = 261;
+ public static final int NOTIFICATION_ZEN_MODE_VISUAL_INTERRUPTIONS = 262;
+ public static final int ACTION_ZEN_ALLOW_PEEK = 263;
+ public static final int ACTION_ZEN_ALLOW_LIGHTS = 264;
+ public static final int NOTIFICATION_TOPIC_NOTIFICATION = 265;
+ public static final int ACTION_DEFAULT_SMS_APP_CHANGED = 266;
+ public static final int QS_COLOR_MATRIX = 267;
+ public static final int QS_CUSTOM = 268;
+ public static final int ACTION_ZEN_ALLOW_SCREEN_ON = 269;
- // These constants must match those in the analytic pipeline, do not edit.
- // Add temporary values to the top of MetricsLogger instead.
+ /**
+ * Logged when the user docks a window from recents by longpressing a task and dragging it to
+ * the dock area.
+ */
+ public static final int ACTION_WINDOW_DOCK_DRAG_DROP = 270;
- //aliases
- public static final int DEVICEINFO_STORAGE = DEVICEINFO_MEMORY;
+ /**
+ * Logged when the user docks a fullscreen window by long pressing recents which also opens
+ * recents on the lower/right side.
+ */
+ public static final int ACTION_WINDOW_DOCK_LONGPRESS = 271;
+
+ /**
+ * Logged when the user docks a window by dragging from the navbar which also opens recents on
+ * the lower/right side.
+ */
+ public static final int ACTION_WINDOW_DOCK_SWIPE = 272;
+
+ /**
+ * Logged when the user launches a profile-specific app and we intercept it with the confirm
+ * credentials UI.
+ */
+ public static final int PROFILE_CHALLENGE = 273;
+ public static final int QS_BATTERY_DETAIL = 274;
+
+ /**
+ * Logged when the user goes into the overview history.
+ */
+ public static final int OVERVIEW_HISTORY = 275;
+
+ /**
+ * Logged when the user pages through overview.
+ */
+ public static final int ACTION_OVERVIEW_PAGE = 276;
+
+ /**
+ * Logged when the user launches a task from overview.
+ */
+ public static final int ACTION_OVERVIEW_SELECT = 277;
}
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index b0b25d3..183d8d7 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -15,69 +15,21 @@
*/
package com.android.internal.logging;
-
import android.content.Context;
import android.os.Build;
import android.view.View;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+
/**
* Log all the things.
*
* @hide
*/
-public class MetricsLogger implements MetricsConstants {
- // Temporary constants go here, to await migration to MetricsConstants.
- public static final int QS_USER_TILE = 258;
- public static final int QS_BATTERY_TILE = 259;
- public static final int NOTIFICATION_ZEN_MODE_VISUAL_INTERRUPTIONS = 260;
- public static final int ACTION_ZEN_ALLOW_PEEK = 261;
- public static final int ACTION_ZEN_ALLOW_LIGHTS = 262;
- public static final int NOTIFICATION_TOPIC_NOTIFICATION = 263;
- public static final int ACTION_DEFAULT_SMS_APP_CHANGED = 264;
- public static final int QS_COLOR_MATRIX = 265;
- public static final int QS_CUSTOM = 266;
- public static final int ACTION_ZEN_ALLOW_SCREEN_ON = 267;
+public class MetricsLogger implements com.android.internal.logging.MetricsConstants {
+ // define metric categories in frameworks/base/core/proto/src/metrics_constants.proto.
- /**
- * Logged when the user docks a window from recents by longpressing a task and dragging it to
- * the dock area.
- */
- public static final int ACTION_WINDOW_DOCK_DRAG_DROP = 268;
-
- /**
- * Logged when the user docks a fullscreen window by long pressing recents which also opens
- * recents on the lower/right side.
- */
- public static final int ACTION_WINDOW_DOCK_LONGPRESS = 269;
-
- /**
- * Logged when the user docks a window by dragging from the navbar which also opens recents on
- * the lower/right side.
- */
- public static final int ACTION_WINDOW_DOCK_SWIPE = 270;
-
- /**
- * Logged when the user launches a profile-specific app and we intercept it with the confirm
- * credentials UI.
- */
- public static final int PROFILE_CHALLENGE = 271;
- public static final int QS_BATTERY_DETAIL = 272;
-
- /**
- * Logged when the user goes into the overview history.
- */
- public static final int OVERVIEW_HISTORY = 273;
-
- /**
- * Logged when the user pages through overview.
- */
- public static final int ACTION_OVERVIEW_PAGE = 274;
-
- /**
- * Logged when the user launches a task from overview.
- */
- public static final int ACTION_OVERVIEW_SELECT = 275;
-
+ public static final int VIEW_UNKNOWN = MetricsEvent.VIEW_UNKNOWN;
public static void visible(Context context, int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 5aa6a73..edced56 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -28,14 +28,18 @@
#include <EGL/eglext.h>
#include <EGL/egl_cache.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
#include <android_runtime/android_view_Surface.h>
#include <system/window.h>
#include "android_view_GraphicBuffer.h"
+#include "android_os_MessageQueue.h"
#include <Animator.h>
#include <AnimationContext.h>
+#include <FrameInfo.h>
#include <IContextFactory.h>
#include <JankTracker.h>
#include <RenderNode.h>
@@ -50,6 +54,12 @@
using namespace android::uirenderer;
using namespace android::uirenderer::renderthread;
+struct {
+ jfieldID buffer;
+ jfieldID messageQueue;
+ jmethodID notifyData;
+} gFrameStatsObserverClassInfo;
+
static JNIEnv* getenv(JavaVM* vm) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -207,6 +217,99 @@
RootRenderNode* mRootNode;
};
+class ObserverProxy;
+
+class NotifyHandler : public MessageHandler {
+public:
+ NotifyHandler(JavaVM* vm) : mVm(vm) {}
+
+ void setObserver(ObserverProxy* observer) {
+ mObserver = observer;
+ }
+
+ void setBuffer(BufferPool::Buffer* buffer) {
+ mBuffer = buffer;
+ }
+
+ virtual void handleMessage(const Message& message);
+
+private:
+ JavaVM* mVm;
+
+ sp<ObserverProxy> mObserver;
+ BufferPool::Buffer* mBuffer;
+};
+
+class ObserverProxy : public FrameStatsObserver {
+public:
+ ObserverProxy(JavaVM *vm, jobject fso) : mVm(vm) {
+ JNIEnv* env = getenv(mVm);
+
+ jlongArray longArrayLocal = env->NewLongArray(kBufferSize);
+ LOG_ALWAYS_FATAL_IF(longArrayLocal == nullptr,
+ "OOM: can't allocate frame stats buffer");
+ env->SetObjectField(fso, gFrameStatsObserverClassInfo.buffer, longArrayLocal);
+
+ mFsoWeak = env->NewWeakGlobalRef(fso);
+ LOG_ALWAYS_FATAL_IF(mFsoWeak == nullptr,
+ "unable to create frame stats observer reference");
+
+ jobject messageQueueLocal =
+ env->GetObjectField(fso, gFrameStatsObserverClassInfo.messageQueue);
+ mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal);
+ LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available");
+
+ mMessageHandler = new NotifyHandler(mVm);
+ LOG_ALWAYS_FATAL_IF(mMessageHandler == nullptr,
+ "OOM: unable to allocate NotifyHandler");
+ }
+
+ ~ObserverProxy() {
+ JNIEnv* env = getenv(mVm);
+ env->DeleteWeakGlobalRef(mFsoWeak);
+ }
+
+ jweak getJavaObjectRef() {
+ return mFsoWeak;
+ }
+
+ virtual void notify(BufferPool::Buffer* buffer) {
+ buffer->incRef();
+ mMessageHandler->setBuffer(buffer);
+ mMessageHandler->setObserver(this);
+ mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage);
+ }
+
+private:
+ static const int kBufferSize = static_cast<int>(FrameInfoIndex::NumIndexes);
+
+ JavaVM* mVm;
+ jweak mFsoWeak;
+
+ sp<MessageQueue> mMessageQueue;
+ sp<NotifyHandler> mMessageHandler;
+ Message mMessage;
+};
+
+void NotifyHandler::handleMessage(const Message& message) {
+ JNIEnv* env = getenv(mVm);
+
+ jobject target = env->NewLocalRef(mObserver->getJavaObjectRef());
+
+ if (target != nullptr) {
+ jobject javaBuffer = env->GetObjectField(target, gFrameStatsObserverClassInfo.buffer);
+ if (javaBuffer != nullptr) {
+ env->SetLongArrayRegion(reinterpret_cast<jlongArray>(javaBuffer),
+ 0, mBuffer->getSize(), mBuffer->getBuffer());
+ env->CallVoidMethod(target, gFrameStatsObserverClassInfo.notifyData);
+ env->DeleteLocalRef(target);
+ }
+ }
+
+ mBuffer->release();
+ mObserver.clear();
+}
+
static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz,
jlong proxyPtr, jobject graphicBuffer, jlongArray atlasMapArray) {
sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer);
@@ -468,6 +571,42 @@
}
// ----------------------------------------------------------------------------
+// FrameStatsObserver
+// ----------------------------------------------------------------------------
+
+static jlong android_view_ThreadedRenderer_addFrameStatsObserver(JNIEnv* env,
+ jclass clazz, jlong proxyPtr, jobject fso) {
+ JavaVM* vm = nullptr;
+ if (env->GetJavaVM(&vm) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Unable to get Java VM");
+ return 0;
+ }
+
+ renderthread::RenderProxy* renderProxy =
+ reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
+
+ FrameStatsObserver* observer = new ObserverProxy(vm, fso);
+ renderProxy->addFrameStatsObserver(observer);
+ return reinterpret_cast<jlong>(observer);
+}
+
+static void android_view_ThreadedRenderer_removeFrameStatsObserver(JNIEnv* env, jclass clazz,
+ jlong proxyPtr, jlong observerPtr) {
+ FrameStatsObserver* observer = reinterpret_cast<FrameStatsObserver*>(observerPtr);
+ renderthread::RenderProxy* renderProxy =
+ reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
+
+ renderProxy->removeFrameStatsObserver(observer);
+}
+
+static jint android_view_ThreadedRenderer_getDroppedFrameReportCount(JNIEnv* env, jclass clazz,
+ jlong proxyPtr) {
+ renderthread::RenderProxy* renderProxy =
+ reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
+ return renderProxy->getDroppedFrameReportCount();
+}
+
+// ----------------------------------------------------------------------------
// Shaders
// ----------------------------------------------------------------------------
@@ -523,9 +662,26 @@
{ "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
{ "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
{ "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
+ { "nAddFrameStatsObserver",
+ "(JLandroid/view/FrameStatsObserver;)J",
+ (void*)android_view_ThreadedRenderer_addFrameStatsObserver },
+ { "nRemoveFrameStatsObserver",
+ "(JJ)V",
+ (void*)android_view_ThreadedRenderer_removeFrameStatsObserver },
+ { "nGetDroppedFrameReportCount",
+ "(J)J",
+ (void*)android_view_ThreadedRenderer_getDroppedFrameReportCount },
};
int register_android_view_ThreadedRenderer(JNIEnv* env) {
+ jclass clazz = FindClassOrDie(env, "android/view/FrameStatsObserver");
+ gFrameStatsObserverClassInfo.messageQueue =
+ GetFieldIDOrDie(env, clazz, "mMessageQueue", "Landroid/os/MessageQueue;");
+ gFrameStatsObserverClassInfo.buffer =
+ GetFieldIDOrDie(env, clazz, "mBuffer", "[J");
+ gFrameStatsObserverClassInfo.notifyData =
+ GetMethodIDOrDie(env, clazz, "notifyDataAvailable", "()V");
+
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 8ba6318..483ccf7 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -235,7 +235,8 @@
tests/unit/LinearAllocatorTests.cpp \
tests/unit/VectorDrawableTests.cpp \
tests/unit/OffscreenBufferPoolTests.cpp \
- tests/unit/StringUtilsTests.cpp
+ tests/unit/StringUtilsTests.cpp \
+ tests/unit/BufferPoolTests.cpp
ifeq (true, $(HWUI_NEW_OPS))
LOCAL_SRC_FILES += \
diff --git a/libs/hwui/BufferPool.h b/libs/hwui/BufferPool.h
new file mode 100644
index 0000000..9bda233
--- /dev/null
+++ b/libs/hwui/BufferPool.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/RefBase.h>
+#include <utils/Log.h>
+
+#include <atomic>
+#include <stdint.h>
+#include <memory>
+#include <mutex>
+
+namespace android {
+namespace uirenderer {
+
+/*
+ * Simple thread-safe pool of int64_t arrays of a provided size.
+ *
+ * Permits allocating a client-provided max number of buffers.
+ * If all buffers are in use, refuses to service any more
+ * acquire requests until buffers are re-released to the pool.
+ */
+class BufferPool : public VirtualLightRefBase {
+public:
+ class Buffer {
+ public:
+ int64_t* getBuffer() { return mBuffer.get(); }
+ size_t getSize() { return mSize; }
+
+ void release() {
+ LOG_ALWAYS_FATAL_IF(mPool.get() == nullptr, "attempt to release unacquired buffer");
+ mPool->release(this);
+ }
+
+ Buffer* incRef() {
+ mRefs++;
+ return this;
+ }
+
+ int decRef() {
+ int refs = mRefs.fetch_sub(1);
+ LOG_ALWAYS_FATAL_IF(refs == 0, "buffer reference decremented below 0");
+ return refs - 1;
+ }
+
+ private:
+ friend class BufferPool;
+
+ Buffer(BufferPool* pool, size_t size) {
+ mSize = size;
+ mBuffer.reset(new int64_t[size]);
+ mPool = pool;
+ mRefs++;
+ }
+
+ void setPool(BufferPool* pool) {
+ mPool = pool;
+ }
+
+ std::unique_ptr<Buffer> mNext;
+ std::unique_ptr<int64_t[]> mBuffer;
+ sp<BufferPool> mPool;
+ size_t mSize;
+
+ std::atomic_int mRefs;
+ };
+
+ BufferPool(size_t bufferSize, size_t count)
+ : mBufferSize(bufferSize), mCount(count) {}
+
+ /**
+ * Acquires a buffer from the buffer pool if available.
+ *
+ * Only `mCount` buffers are allowed to be in use at a single
+ * instance.
+ *
+ * If no buffer is available, i.e. `mCount` buffers are in use,
+ * returns nullptr.
+ *
+ * The pointer returned from this method *MUST NOT* be freed, instead
+ * BufferPool::release() must be called upon it when the client
+ * is done with it. Failing to release buffers will eventually make the
+ * BufferPool refuse to service any more BufferPool::acquire() requests.
+ */
+ BufferPool::Buffer* acquire() {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ if (mHead.get() != nullptr) {
+ BufferPool::Buffer* res = mHead.release();
+ mHead = std::move(res->mNext);
+ res->mNext.reset(nullptr);
+ res->setPool(this);
+ res->incRef();
+ return res;
+ }
+
+ if (mAllocatedCount < mCount) {
+ ++mAllocatedCount;
+ return new BufferPool::Buffer(this, mBufferSize);
+ }
+
+ return nullptr;
+ }
+
+ /**
+ * Releases a buffer previously acquired by BufferPool::acquire().
+ *
+ * The released buffer is not valid after calling this method and
+ * attempting to use will result in undefined behavior.
+ */
+ void release(BufferPool::Buffer* buffer) {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ if (buffer->decRef() != 0) {
+ return;
+ }
+
+ buffer->setPool(nullptr);
+
+ BufferPool::Buffer* list = mHead.get();
+ if (list == nullptr) {
+ mHead.reset(buffer);
+ mHead->mNext.reset(nullptr);
+ return;
+ }
+
+ while (list->mNext.get() != nullptr) {
+ list = list->mNext.get();
+ }
+
+ list->mNext.reset(buffer);
+ }
+
+ /*
+ * Used for testing.
+ */
+ size_t getAvailableBufferCount() {
+ size_t remainingToAllocateCount = mCount - mAllocatedCount;
+
+ BufferPool::Buffer* list = mHead.get();
+ if (list == nullptr) return remainingToAllocateCount;
+
+ int count = 1;
+ while (list->mNext.get() != nullptr) {
+ count++;
+ list = list->mNext.get();
+ }
+
+ return count + remainingToAllocateCount;
+ }
+
+private:
+ mutable std::mutex mLock;
+
+ size_t mBufferSize;
+ size_t mCount;
+ size_t mAllocatedCount = 0;
+ std::unique_ptr<BufferPool::Buffer> mHead;
+};
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index f8013ab..0baca39 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -118,6 +118,10 @@
set(FrameInfoIndex::Flags) |= static_cast<uint64_t>(frameInfoFlag);
}
+ const int64_t* data() const {
+ return mFrameInfo;
+ }
+
inline int64_t operator[](FrameInfoIndex index) const {
return get(index);
}
diff --git a/libs/hwui/FrameStatsObserver.h b/libs/hwui/FrameStatsObserver.h
new file mode 100644
index 0000000..7abc9f1
--- /dev/null
+++ b/libs/hwui/FrameStatsObserver.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/RefBase.h>
+
+#include "BufferPool.h"
+
+namespace android {
+namespace uirenderer {
+
+class FrameStatsObserver : public VirtualLightRefBase {
+public:
+ virtual void notify(BufferPool::Buffer* buffer);
+};
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/FrameStatsReporter.h b/libs/hwui/FrameStatsReporter.h
new file mode 100644
index 0000000..b8a9432
--- /dev/null
+++ b/libs/hwui/FrameStatsReporter.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/RefBase.h>
+#include <utils/Log.h>
+
+#include "BufferPool.h"
+#include "FrameInfo.h"
+#include "FrameStatsObserver.h"
+
+#include <string.h>
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+class FrameStatsReporter {
+public:
+ FrameStatsReporter() {
+ mBufferPool = new BufferPool(kBufferSize, kBufferCount);
+ LOG_ALWAYS_FATAL_IF(mBufferPool.get() == nullptr, "OOM: unable to allocate buffer pool");
+ }
+
+ void addObserver(FrameStatsObserver* observer) {
+ mObservers.push_back(observer);
+ }
+
+ bool removeObserver(FrameStatsObserver* observer) {
+ for (size_t i = 0; i < mObservers.size(); i++) {
+ if (mObservers[i].get() == observer) {
+ mObservers.erase(mObservers.begin() + i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool hasObservers() {
+ return mObservers.size() > 0;
+ }
+
+ void reportFrameStats(const int64_t* stats) {
+ BufferPool::Buffer* statsBuffer = mBufferPool->acquire();
+
+ if (statsBuffer != nullptr) {
+ // copy in frame stats
+ memcpy(statsBuffer->getBuffer(), stats, kBufferSize * sizeof(*stats));
+
+ // notify on requested threads
+ for (size_t i = 0; i < mObservers.size(); i++) {
+ mObservers[i]->notify(statsBuffer);
+ }
+
+ // drop our reference
+ statsBuffer->release();
+ } else {
+ mDroppedReports++;
+ }
+ }
+
+ int getDroppedReports() { return mDroppedReports; }
+
+private:
+ static const size_t kBufferCount = 3;
+ static const size_t kBufferSize = static_cast<size_t>(FrameInfoIndex::NumIndexes);
+
+ std::vector< sp<FrameStatsObserver> > mObservers;
+
+ sp<BufferPool> mBufferPool;
+
+ int mDroppedReports = 0;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 6f8d627..cdd2da0 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -505,6 +505,9 @@
mJankTracker.addFrame(*mCurrentFrameInfo);
mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
+ if (CC_UNLIKELY(mFrameStatsReporter.get() != nullptr)) {
+ mFrameStatsReporter->reportFrameStats(mCurrentFrameInfo->data());
+ }
GpuMemoryTracker::onFrameCompleted();
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 8e64cbb..270fb1f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -20,6 +20,7 @@
#include "DamageAccumulator.h"
#include "FrameInfo.h"
#include "FrameInfoVisualizer.h"
+#include "FrameStatsReporter.h"
#include "IContextFactory.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
@@ -139,6 +140,31 @@
return mRenderThread.renderState();
}
+ void addFrameStatsObserver(FrameStatsObserver* observer) {
+ if (mFrameStatsReporter.get() == nullptr) {
+ mFrameStatsReporter.reset(new FrameStatsReporter());
+ }
+
+ mFrameStatsReporter->addObserver(observer);
+ }
+
+ void removeFrameStatsObserver(FrameStatsObserver* observer) {
+ if (mFrameStatsReporter.get() != nullptr) {
+ mFrameStatsReporter->removeObserver(observer);
+ if (!mFrameStatsReporter->hasObservers()) {
+ mFrameStatsReporter.reset(nullptr);
+ }
+ }
+ }
+
+ long getDroppedFrameReportCount() {
+ if (mFrameStatsReporter.get() != nullptr) {
+ return mFrameStatsReporter->getDroppedReports();
+ }
+
+ return 0;
+ }
+
private:
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -187,6 +213,7 @@
std::string mName;
JankTracker mJankTracker;
FrameInfoVisualizer mProfiler;
+ std::unique_ptr<FrameStatsReporter> mFrameStatsReporter;
std::set<RenderNode*> mPrefetechedLayers;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index db2a2c8..1d1b144 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -568,6 +568,54 @@
post(task);
}
+CREATE_BRIDGE2(addFrameStatsObserver, CanvasContext* context,
+ FrameStatsObserver* frameStatsObserver) {
+ args->context->addFrameStatsObserver(args->frameStatsObserver);
+ if (args->frameStatsObserver != nullptr) {
+ args->frameStatsObserver->decStrong(args->context);
+ }
+ return nullptr;
+}
+
+void RenderProxy::addFrameStatsObserver(FrameStatsObserver* observer) {
+ SETUP_TASK(addFrameStatsObserver);
+ args->context = mContext;
+ args->frameStatsObserver = observer;
+ if (observer != nullptr) {
+ observer->incStrong(mContext);
+ }
+ post(task);
+}
+
+CREATE_BRIDGE2(removeFrameStatsObserver, CanvasContext* context,
+ FrameStatsObserver* frameStatsObserver) {
+ args->context->removeFrameStatsObserver(args->frameStatsObserver);
+ if (args->frameStatsObserver != nullptr) {
+ args->frameStatsObserver->decStrong(args->context);
+ }
+ return nullptr;
+}
+
+void RenderProxy::removeFrameStatsObserver(FrameStatsObserver* observer) {
+ SETUP_TASK(removeFrameStatsObserver);
+ args->context = mContext;
+ args->frameStatsObserver = observer;
+ if (observer != nullptr) {
+ observer->incStrong(mContext);
+ }
+ post(task);
+}
+
+CREATE_BRIDGE1(getDroppedFrameReportCount, CanvasContext* context) {
+ return (void*) args->context->getDroppedFrameReportCount();
+}
+
+long RenderProxy::getDroppedFrameReportCount() {
+ SETUP_TASK(getDroppedFrameReportCount);
+ args->context = mContext;
+ return (long) postAndWait(task);
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 0f91b2a..4180d802 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -29,6 +29,7 @@
#include <utils/StrongPointer.h>
#include "../Caches.h"
+#include "../FrameStatsObserver.h"
#include "../IContextFactory.h"
#include "CanvasContext.h"
#include "DrawFrameTask.h"
@@ -112,6 +113,10 @@
ANDROID_API void drawRenderNode(RenderNode* node);
ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom);
+ ANDROID_API void addFrameStatsObserver(FrameStatsObserver* observer);
+ ANDROID_API void removeFrameStatsObserver(FrameStatsObserver* observer);
+ ANDROID_API long getDroppedFrameReportCount();
+
private:
RenderThread& mRenderThread;
CanvasContext* mContext;
diff --git a/libs/hwui/tests/unit/BufferPoolTests.cpp b/libs/hwui/tests/unit/BufferPoolTests.cpp
new file mode 100644
index 0000000..09bd302
--- /dev/null
+++ b/libs/hwui/tests/unit/BufferPoolTests.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <BufferPool.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace uirenderer {
+
+TEST(BufferPool, acquireThenRelease) {
+ static const int numRuns = 5;
+
+ // 10 buffers of size 1
+ static const size_t bufferSize = 1;
+ static const size_t bufferCount = 10;
+ sp<BufferPool> pool = new BufferPool(bufferSize, bufferCount);
+
+ for (int run = 0; run < numRuns; run++) {
+ BufferPool::Buffer* acquiredBuffers[bufferCount];
+ for (size_t i = 0; i < bufferCount; i++) {
+ ASSERT_EQ(bufferCount - i, pool->getAvailableBufferCount());
+ acquiredBuffers[i] = pool->acquire();
+ ASSERT_NE(nullptr, acquiredBuffers[i]);
+ }
+
+ for (size_t i = 0; i < bufferCount; i++) {
+ ASSERT_EQ(i, pool->getAvailableBufferCount());
+ acquiredBuffers[i]->release();
+ acquiredBuffers[i] = nullptr;
+ }
+
+ ASSERT_EQ(bufferCount, pool->getAvailableBufferCount());
+ }
+}
+
+TEST(BufferPool, acquireReleaseInterleaved) {
+ static const int numRuns = 5;
+
+ // 10 buffers of size 1
+ static const size_t bufferSize = 1;
+ static const size_t bufferCount = 10;
+
+ sp<BufferPool> pool = new BufferPool(bufferSize, bufferCount);
+
+ for (int run = 0; run < numRuns; run++) {
+ BufferPool::Buffer* acquiredBuffers[bufferCount];
+
+ // acquire all
+ for (size_t i = 0; i < bufferCount; i++) {
+ ASSERT_EQ(bufferCount - i, pool->getAvailableBufferCount());
+ acquiredBuffers[i] = pool->acquire();
+ ASSERT_NE(nullptr, acquiredBuffers[i]);
+ }
+
+ // release half
+ for (size_t i = 0; i < bufferCount / 2; i++) {
+ ASSERT_EQ(i, pool->getAvailableBufferCount());
+ acquiredBuffers[i]->release();
+ acquiredBuffers[i] = nullptr;
+ }
+
+ const size_t expectedRemaining = bufferCount / 2;
+ ASSERT_EQ(expectedRemaining, pool->getAvailableBufferCount());
+
+ // acquire half
+ for (size_t i = 0; i < bufferCount / 2; i++) {
+ ASSERT_EQ(expectedRemaining - i, pool->getAvailableBufferCount());
+ acquiredBuffers[i] = pool->acquire();
+ }
+
+ // acquire one more, should fail
+ ASSERT_EQ(nullptr, pool->acquire());
+
+ // release all
+ for (size_t i = 0; i < bufferCount; i++) {
+ ASSERT_EQ(i, pool->getAvailableBufferCount());
+ acquiredBuffers[i]->release();
+ acquiredBuffers[i] = nullptr;
+ }
+
+ ASSERT_EQ(bufferCount, pool->getAvailableBufferCount());
+ }
+}
+
+};
+};
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index dcc4946..b0431ce 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -138,8 +138,8 @@
// Move the pixels into the destination SkBitmap
- SK_ALWAYSBREAK(nativeBuffer.format == android::PIXEL_FORMAT_RGBA_8888 &&
- "Native buffer not RGBA!");
+ LOG_ALWAYS_FATAL_IF(nativeBuffer.format != android::PIXEL_FORMAT_RGBA_8888,
+ "Native buffer not RGBA!");
SkImageInfo nativeConfig =
SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height,
kRGBA_8888_SkColorType, kPremul_SkAlphaType);
@@ -153,8 +153,8 @@
return false;
}
- SK_ALWAYSBREAK(bmp->colorType() == kRGBA_8888_SkColorType &&
- "Destination buffer not RGBA!");
+ LOG_ALWAYS_FATAL_IF(bmp->colorType() != kRGBA_8888_SkColorType,
+ "Destination buffer not RGBA!");
success =
nativeWrapper.readPixels(destinationConfig, bmp->getPixels(), bmp->rowBytes(), 0, 0);
if (!success) {
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 61cad2f..e4d0fec 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -11,7 +11,8 @@
android-support-v7-recyclerview \
android-support-v7-preference \
android-support-v7-appcompat \
- android-support-v14-preference
+ android-support-v14-preference \
+ framework-protos
LOCAL_JAVA_LIBRARIES := telephony-common
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b5f146b..8979023 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -34,7 +34,9 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile.DetailAdapter;
@@ -245,7 +247,7 @@
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
- MetricsLogger.visibility(mContext, MetricsLogger.QS_PANEL, mExpanded);
+ MetricsLogger.visibility(mContext, MetricsEvent.QS_PANEL, mExpanded);
if (!mExpanded) {
closeDetail();
} else {
@@ -498,7 +500,7 @@
int newVis = visible ? VISIBLE : INVISIBLE;
mQsContainer.setVisibility(newVis);
if (mGridContentVisible != visible) {
- MetricsLogger.visibility(mContext, MetricsLogger.QS_PANEL, newVis);
+ MetricsLogger.visibility(mContext, MetricsEvent.QS_PANEL, newVis);
}
mGridContentVisible = visible;
}
diff --git a/proto/Android.mk b/proto/Android.mk
new file mode 100644
index 0000000..a13a780
--- /dev/null
+++ b/proto/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := framework-protos
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+LOCAL_SRC_FILES:= $(call all-proto-files-under, src)
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
+
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_LIBRARIES := core-oj core-libart
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/jarjar-rules.txt
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/proto/jarjar-rules.txt b/proto/jarjar-rules.txt
new file mode 100644
index 0000000..0c77c2a
--- /dev/null
+++ b/proto/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.** com.android.@1
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
new file mode 100644
index 0000000..fa3a47d
--- /dev/null
+++ b/proto/src/metrics_constants.proto
@@ -0,0 +1,325 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+
+option java_package = "com.android.internal.logging";
+option java_outer_classname = "MetricsProto";
+
+package com_android_internal_logging;
+
+// Wrapper for System UI log events
+message MetricsEvent {
+
+ // Known visual elements: views or controls.
+ enum View {
+ VIEW_UNKNOWN = 0;
+ MAIN_SETTINGS = 1;
+ ACCESSIBILITY = 2;
+ ACCESSIBILITY_CAPTION_PROPERTIES = 3;
+ ACCESSIBILITY_SERVICE = 4;
+ ACCESSIBILITY_TOGGLE_DALTONIZER = 5;
+ ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE = 6;
+ ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 7;
+ ACCOUNT = 8;
+ ACCOUNTS_ACCOUNT_SYNC = 9;
+ ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY = 10;
+ ACCOUNTS_MANAGE_ACCOUNTS = 11;
+ APN = 12;
+ APN_EDITOR = 13;
+ APP_OPS_DETAILS = 14;
+ APP_OPS_SUMMARY = 15;
+ APPLICATION = 16;
+ APPLICATIONS_APP_LAUNCH = 17;
+ APPLICATIONS_APP_PERMISSION = 18;
+ APPLICATIONS_APP_STORAGE = 19;
+ APPLICATIONS_INSTALLED_APP_DETAILS = 20;
+ APPLICATIONS_PROCESS_STATS_DETAIL = 21;
+ APPLICATIONS_PROCESS_STATS_MEM_DETAIL = 22;
+ APPLICATIONS_PROCESS_STATS_UI = 23;
+ BLUETOOTH = 24;
+ BLUETOOTH_DEVICE_PICKER = 25;
+ BLUETOOTH_DEVICE_PROFILES = 26;
+ CHOOSE_LOCK_GENERIC = 27;
+ CHOOSE_LOCK_PASSWORD = 28;
+ CHOOSE_LOCK_PATTERN = 29;
+ CONFIRM_LOCK_PASSWORD = 30;
+ CONFIRM_LOCK_PATTERN = 31;
+ CRYPT_KEEPER = 32;
+ CRYPT_KEEPER_CONFIRM = 33;
+ DASHBOARD_SEARCH_RESULTS = 34;
+ DASHBOARD_SUMMARY = 35;
+ DATA_USAGE = 36;
+ DATA_USAGE_SUMMARY = 37;
+ DATE_TIME = 38;
+ DEVELOPMENT = 39;
+ DEVICEINFO = 40;
+ DEVICEINFO_IMEI_INFORMATION = 41;
+ DEVICEINFO_STORAGE = 42;
+ DEVICEINFO_SIM_STATUS = 43;
+ DEVICEINFO_STATUS = 44;
+ DEVICEINFO_USB = 45;
+ DISPLAY = 46;
+ DREAM = 47;
+ ENCRYPTION = 48;
+ FINGERPRINT = 49;
+ FINGERPRINT_ENROLL = 50;
+ FUELGAUGE_BATTERY_HISTORY_DETAIL = 51;
+ FUELGAUGE_BATTERY_SAVER = 52;
+ FUELGAUGE_POWER_USAGE_DETAIL = 53;
+ FUELGAUGE_POWER_USAGE_SUMMARY = 54;
+ HOME = 55;
+ ICC_LOCK = 56;
+ INPUTMETHOD_LANGUAGE = 57;
+ INPUTMETHOD_KEYBOARD = 58;
+ INPUTMETHOD_SPELL_CHECKERS = 59;
+ INPUTMETHOD_SUBTYPE_ENABLER = 60;
+ INPUTMETHOD_USER_DICTIONARY = 61;
+ INPUTMETHOD_USER_DICTIONARY_ADD_WORD = 62;
+ LOCATION = 63;
+ LOCATION_MODE = 64;
+ MANAGE_APPLICATIONS = 65;
+ MASTER_CLEAR = 66;
+ MASTER_CLEAR_CONFIRM = 67;
+ NET_DATA_USAGE_METERED = 68;
+ NFC_BEAM = 69;
+ NFC_PAYMENT = 70;
+ NOTIFICATION = 71;
+ NOTIFICATION_APP_NOTIFICATION = 72;
+ NOTIFICATION_OTHER_SOUND = 73;
+ NOTIFICATION_REDACTION = 74;
+ NOTIFICATION_STATION = 75;
+ NOTIFICATION_ZEN_MODE = 76;
+ OWNER_INFO = 77;
+ PRINT_JOB_SETTINGS = 78;
+ PRINT_SERVICE_SETTINGS = 79;
+ PRINT_SETTINGS = 80;
+ PRIVACY = 81;
+ PROXY_SELECTOR = 82;
+ RESET_NETWORK = 83;
+ RESET_NETWORK_CONFIRM = 84;
+ RUNNING_SERVICE_DETAILS = 85;
+ SCREEN_PINNING = 86;
+ SECURITY = 87;
+ SIM = 88;
+ TESTING = 89;
+ TETHER = 90;
+ TRUST_AGENT = 91;
+ TRUSTED_CREDENTIALS = 92;
+ TTS_ENGINE_SETTINGS = 93;
+ TTS_TEXT_TO_SPEECH = 94;
+ USAGE_ACCESS = 95;
+ USER = 96;
+ USERS_APP_RESTRICTIONS = 97;
+ USER_DETAILS = 98;
+ VOICE_INPUT = 99;
+ VPN = 100;
+ WALLPAPER_TYPE = 101;
+ WFD_WIFI_DISPLAY = 102;
+ WIFI = 103;
+ WIFI_ADVANCED = 104;
+ WIFI_CALLING = 105;
+ WIFI_SAVED_ACCESS_POINTS = 106;
+ WIFI_APITEST = 107;
+ WIFI_INFO = 108;
+ WIFI_P2P = 109;
+ WIRELESS = 110;
+ QS_PANEL = 111;
+ QS_AIRPLANEMODE = 112;
+ QS_BLUETOOTH = 113;
+ QS_CAST = 114;
+ QS_CELLULAR = 115;
+ QS_COLORINVERSION = 116;
+ QS_DATAUSAGEDETAIL = 117;
+ QS_DND = 118;
+ QS_FLASHLIGHT = 119;
+ QS_HOTSPOT = 120;
+ QS_INTENT = 121;
+ QS_LOCATION = 122;
+ QS_ROTATIONLOCK = 123;
+ QS_USERDETAILITE = 124;
+ QS_USERDETAIL = 125;
+ QS_WIFI = 126;
+ NOTIFICATION_PANEL = 127;
+ NOTIFICATION_ITEM = 128;
+ NOTIFICATION_ITEM_ACTION = 129;
+ APPLICATIONS_ADVANCED = 130;
+ LOCATION_SCANNING = 131;
+ MANAGE_APPLICATIONS_ALL = 132;
+ MANAGE_APPLICATIONS_NOTIFICATIONS = 133;
+ ACTION_WIFI_ADD_NETWORK = 134;
+ ACTION_WIFI_CONNECT = 135;
+ ACTION_WIFI_FORCE_SCAN = 136;
+ ACTION_WIFI_FORGET = 137;
+ ACTION_WIFI_OFF = 138;
+ ACTION_WIFI_ON = 139;
+ MANAGE_PERMISSIONS = 140;
+ NOTIFICATION_ZEN_MODE_PRIORITY = 141;
+ NOTIFICATION_ZEN_MODE_AUTOMATION = 142;
+ MANAGE_DOMAIN_URLS = 143;
+ NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144;
+ NOTIFICATION_ZEN_MODE_EXTERNAL_RULE = 145;
+ NOTIFICATION_ZEN_MODE_EVENT_RULE = 146;
+ ACTION_BAN_APP_NOTES = 147;
+ ACTION_DISMISS_ALL_NOTES = 148;
+ QS_DND_DETAILS = 149;
+ QS_BLUETOOTH_DETAILS = 150;
+ QS_CAST_DETAILS = 151;
+ QS_WIFI_DETAILS = 152;
+ QS_WIFI_TOGGLE = 153;
+ QS_BLUETOOTH_TOGGLE = 154;
+ QS_CELLULAR_TOGGLE = 155;
+ QS_SWITCH_USER = 156;
+ QS_CAST_SELECT = 157;
+ QS_CAST_DISCONNECT = 158;
+ ACTION_BLUETOOTH_TOGGLE = 159;
+ ACTION_BLUETOOTH_SCAN = 160;
+ ACTION_BLUETOOTH_RENAME = 161;
+ ACTION_BLUETOOTH_FILES = 162;
+ QS_DND_TIME = 163;
+ QS_DND_CONDITION_SELECT = 164;
+ QS_DND_ZEN_SELECT = 165;
+ QS_DND_TOGGLE = 166;
+ ACTION_ZEN_ALLOW_REMINDERS = 167;
+ ACTION_ZEN_ALLOW_EVENTS = 168;
+ ACTION_ZEN_ALLOW_MESSAGES = 169;
+ ACTION_ZEN_ALLOW_CALLS = 170;
+ ACTION_ZEN_ALLOW_REPEAT_CALLS = 171;
+ ACTION_ZEN_ADD_RULE = 172;
+ ACTION_ZEN_ADD_RULE_OK = 173;
+ ACTION_ZEN_DELETE_RULE = 174;
+ ACTION_ZEN_DELETE_RULE_OK = 175;
+ ACTION_ZEN_ENABLE_RULE = 176;
+ ACTION_AIRPLANE_TOGGLE = 177;
+ ACTION_CELL_DATA_TOGGLE = 178;
+ NOTIFICATION_ACCESS = 179;
+ NOTIFICATION_ZEN_MODE_ACCESS = 180;
+ APPLICATIONS_DEFAULT_APPS = 181;
+ APPLICATIONS_STORAGE_APPS = 182;
+ APPLICATIONS_USAGE_ACCESS_DETAIL = 183;
+ APPLICATIONS_HIGH_POWER_APPS = 184;
+ FUELGAUGE_HIGH_POWER_DETAILS = 185;
+ ACTION_LS_UNLOCK = 186;
+ ACTION_LS_SHADE = 187;
+ ACTION_LS_HINT = 188;
+ ACTION_LS_CAMERA = 189;
+ ACTION_LS_DIALER = 190;
+ ACTION_LS_LOCK = 191;
+ ACTION_LS_NOTE = 192;
+ ACTION_LS_QS = 193;
+ ACTION_SHADE_QS_PULL = 194;
+ ACTION_SHADE_QS_TAP = 195;
+ LOCKSCREEN = 196;
+ BOUNCER = 197;
+ SCREEN = 198;
+ NOTIFICATION_ALERT = 199;
+ ACTION_EMERGENCY_CALL = 200;
+ APPLICATIONS_MANAGE_ASSIST = 201;
+ PROCESS_STATS_SUMMARY = 202;
+ ACTION_ROTATION_LOCK = 203;
+ ACTION_NOTE_CONTROLS = 204;
+ ACTION_NOTE_INFO = 205;
+ ACTION_APP_NOTE_SETTINGS = 206;
+ VOLUME_DIALOG = 207;
+ VOLUME_DIALOG_DETAILS = 208;
+ ACTION_VOLUME_SLIDER = 209;
+ ACTION_VOLUME_STREAM = 210;
+ ACTION_VOLUME_KEY = 211;
+ ACTION_VOLUME_ICON = 212;
+ ACTION_RINGER_MODE = 213;
+ ACTION_ACTIVITY_CHOOSER_SHOWN = 214;
+ ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET = 215;
+ ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET = 216;
+ ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET = 217;
+ ACTION_BRIGHTNESS = 218;
+ ACTION_BRIGHTNESS_AUTO = 219;
+ BRIGHTNESS_DIALOG = 220;
+ SYSTEM_ALERT_WINDOW_APPS = 221;
+ DREAMING = 222;
+ DOZING = 223;
+ OVERVIEW_ACTIVITY = 224;
+ ABOUT_LEGAL_SETTINGS = 225;
+ ACTION_SEARCH_RESULTS = 226;
+ TUNER = 227;
+ TUNER_QS = 228;
+ TUNER_DEMO_MODE = 229;
+ TUNER_QS_REORDER = 230;
+ TUNER_QS_ADD = 231;
+ TUNER_QS_REMOVE = 232;
+ TUNER_STATUS_BAR_ENABLE = 233;
+ TUNER_STATUS_BAR_DISABLE = 234;
+ TUNER_DEMO_MODE_ENABLED = 235;
+ TUNER_DEMO_MODE_ON = 236;
+ TUNER_BATTERY_PERCENTAGE = 237;
+ FUELGAUGE_INACTIVE_APPS = 238;
+ ACTION_ASSIST_LONG_PRESS = 239;
+ FINGERPRINT_ENROLLING = 240;
+ FINGERPRINT_FIND_SENSOR = 241;
+ FINGERPRINT_ENROLL_FINISH = 242;
+ FINGERPRINT_ENROLL_INTRO = 243;
+ FINGERPRINT_ENROLL_ONBOARD = 244;
+ FINGERPRINT_ENROLL_SIDECAR = 245;
+ FINGERPRINT_ENROLLING_SETUP = 246;
+ FINGERPRINT_FIND_SENSOR_SETUP = 247;
+ FINGERPRINT_ENROLL_FINISH_SETUP = 248;
+ FINGERPRINT_ENROLL_INTRO_SETUP = 249;
+ FINGERPRINT_ENROLL_ONBOARD_SETUP = 250;
+ ACTION_FINGERPRINT_ENROLL = 251;
+ ACTION_FINGERPRINT_AUTH = 252;
+ ACTION_FINGERPRINT_DELETE = 253;
+ ACTION_FINGERPRINT_RENAME = 254;
+ ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE = 255;
+ ACTION_WIGGLE_CAMERA_GESTURE = 256;
+ QS_WORKMODE = 257;
+ BACKGROUND_CHECK_SUMMARY = 258;
+ QS_LOCK_TILE = 259;
+ QS_USER_TILE = 260;
+ QS_BATTERY_TILE = 261;
+ NOTIFICATION_ZEN_MODE_VISUAL_INTERRUPTIONS = 262;
+ ACTION_ZEN_ALLOW_PEEK = 263;
+ ACTION_ZEN_ALLOW_LIGHTS = 264;
+ NOTIFICATION_TOPIC_NOTIFICATION = 265;
+ ACTION_DEFAULT_SMS_APP_CHANGED = 266;
+ QS_COLOR_MATRIX = 267;
+ QS_CUSTOM = 268;
+ ACTION_ZEN_ALLOW_SCREEN_ON = 269;
+
+ // Logged when the user docks a window from recents by
+ // longpressing a task and dragging it to the dock area.
+ ACTION_WINDOW_DOCK_DRAG_DROP = 270;
+
+ // Logged when the user docks a fullscreen window by long pressing
+ // recents which also opens recents on the lower/right side.
+ ACTION_WINDOW_DOCK_LONGPRESS = 271;
+
+ // Logged when the user docks a window by dragging from the navbar
+ // which also opens recents on the lower/right side.
+ ACTION_WINDOW_DOCK_SWIPE = 272;
+
+ // Logged when the user launches a profile-specific app and we
+ // intercept it with the confirm credentials UI.
+ PROFILE_CHALLENGE = 273;
+
+ QS_BATTERY_DETAIL = 274;
+
+ // Logged when the user goes into the overview history.
+ OVERVIEW_HISTORY = 275;
+
+ // Logged when the user pages through overview.
+ ACTION_OVERVIEW_PAGE = 276;
+
+ // Logged when the user launches a task from overview.
+ ACTION_OVERVIEW_SELECT = 277;
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9c24271..1434e5e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -536,6 +536,7 @@
// default actuion automatically. Important for devices without direct input
// devices.
private boolean mShowDialogs = true;
+ private boolean mInVrMode = false;
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;
@@ -2204,7 +2205,15 @@
} break;
case VR_MODE_CHANGE_MSG: {
VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
- vrService.setVrMode(msg.arg1 != 0);
+ final boolean vrMode = msg.arg1 != 0;
+ vrService.setVrMode(vrMode);
+
+ if (mInVrMode != vrMode) {
+ synchronized (ActivityManagerService.this) {
+ mInVrMode = vrMode;
+ mShowDialogs = shouldShowDialogs(mConfiguration, mInVrMode);
+ }
+ }
} break;
}
}
@@ -18439,7 +18448,7 @@
// TODO: If our config changes, should we auto dismiss any currently
// showing dialogs?
- mShowDialogs = shouldShowDialogs(newConfig);
+ mShowDialogs = shouldShowDialogs(newConfig, mInVrMode);
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
@@ -18528,13 +18537,13 @@
* A thought: SystemUI might also want to get told about this, the Power
* dialog / global actions also might want different behaviors.
*/
- private static final boolean shouldShowDialogs(Configuration config) {
+ private static final boolean shouldShowDialogs(Configuration config, boolean inVrMode) {
final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
&& config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
&& config.navigation == Configuration.NAVIGATION_NONAV);
final boolean uiIsNotCarType = !((config.uiMode & Configuration.UI_MODE_TYPE_MASK)
== Configuration.UI_MODE_TYPE_CAR);
- return inputMethodExists && uiIsNotCarType;
+ return inputMethodExists && uiIsNotCarType && !inVrMode;
}
@Override