From 7e84d33602bf8486df5db2fe1b3a2a50b3c8c428 Mon Sep 17 00:00:00 2001 From: Adrian Salido Date: Wed, 5 Dec 2018 17:19:57 -0800 Subject: [PATCH 001/450] TouchLatency: add 60/90hz mode switch Change-Id: I512579875530bd6c305310cd6ba50fcd0037e4d4 --- tests/TouchLatency/app/build.gradle | 2 +- .../touchlatency/TouchLatencyActivity.java | 47 +++++++++++++++++++ .../res/layout/activity_touch_latency.xml | 1 + .../src/main/res/menu/menu_touch_latency.xml | 12 ++++- .../app/src/main/res/values/strings.xml | 1 + 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle index 2594322e3727..04a878896f47 100644 --- a/tests/TouchLatency/app/build.gradle +++ b/tests/TouchLatency/app/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { applicationId "com.prefabulated.touchlatency" - minSdkVersion 21 + minSdkVersion 28 targetSdkVersion 28 versionCode 1 versionName "1.0" diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java index 360c22f832b3..ba77a74974d1 100644 --- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java +++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java @@ -24,11 +24,15 @@ import android.graphics.Paint.Align; import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; +import android.view.Display; +import android.view.Display.Mode; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.os.Trace; +import android.view.Window; +import android.view.WindowManager; import java.math.RoundingMode; import java.text.DecimalFormat; @@ -219,14 +223,30 @@ class TouchLatencyView extends View implements View.OnTouchListener { } public class TouchLatencyActivity extends Activity { + private Mode mDisplayModes[]; + private int mCurrentModeIndex; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Trace.beginSection("TouchLatencyActivity onCreate"); setContentView(R.layout.activity_touch_latency); mTouchView = findViewById(R.id.canvasView); + + WindowManager wm = getWindowManager(); + Display display = wm.getDefaultDisplay(); + mDisplayModes = display.getSupportedModes(); + Mode currentMode = getWindowManager().getDefaultDisplay().getMode(); + + for (int i = 0; i < mDisplayModes.length; i++) { + if (currentMode.getModeId() == mDisplayModes[i].getModeId()) { + mCurrentModeIndex = i; + break; + } + } + Trace.endSection(); } @@ -236,10 +256,35 @@ public class TouchLatencyActivity extends Activity { Trace.beginSection("TouchLatencyActivity onCreateOptionsMenu"); // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_touch_latency, menu); + if (mDisplayModes.length > 1) { + MenuItem menuItem = menu.findItem(R.id.display_mode); + Mode currentMode = getWindowManager().getDefaultDisplay().getMode(); + updateDisplayMode(menuItem, currentMode); + } Trace.endSection(); return true; } + + private void updateDisplayMode(MenuItem menuItem, Mode displayMode) { + int fps = (int) displayMode.getRefreshRate(); + menuItem.setTitle(fps + "hz"); + menuItem.setVisible(true); + } + + public void changeDisplayMode(MenuItem item) { + Window w = getWindow(); + WindowManager.LayoutParams params = w.getAttributes(); + + int modeIndex = (mCurrentModeIndex + 1) % mDisplayModes.length; + params.preferredDisplayModeId = mDisplayModes[modeIndex].getModeId(); + w.setAttributes(params); + + updateDisplayMode(item, mDisplayModes[modeIndex]); + mCurrentModeIndex = modeIndex; + } + + @Override public boolean onOptionsItemSelected(MenuItem item) { Trace.beginSection("TouchLatencyActivity onOptionsItemSelected"); @@ -251,6 +296,8 @@ public class TouchLatencyActivity extends Activity { //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { mTouchView.changeMode(item); + } else if (id == R.id.display_mode) { + changeDisplayMode(item); } Trace.endSection(); diff --git a/tests/TouchLatency/app/src/main/res/layout/activity_touch_latency.xml b/tests/TouchLatency/app/src/main/res/layout/activity_touch_latency.xml index 8d20ff24bfe5..625757610154 100644 --- a/tests/TouchLatency/app/src/main/res/layout/activity_touch_latency.xml +++ b/tests/TouchLatency/app/src/main/res/layout/activity_touch_latency.xml @@ -18,6 +18,7 @@ android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" + android:keepScreenOn="true" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".TouchLatencyActivity"> - + + diff --git a/tests/TouchLatency/app/src/main/res/values/strings.xml b/tests/TouchLatency/app/src/main/res/values/strings.xml index b97f095d501e..771992c8e5d3 100644 --- a/tests/TouchLatency/app/src/main/res/values/strings.xml +++ b/tests/TouchLatency/app/src/main/res/values/strings.xml @@ -17,4 +17,5 @@ Touch Latency Touch + Mode -- GitLab From 017a97825e4b73a96121661aea02a0b84a6a3d6e Mon Sep 17 00:00:00 2001 From: "khmel@google.com" Date: Fri, 28 Sep 2018 11:32:01 -0700 Subject: [PATCH 002/450] Add initial GamePerformance test. This collect first set of metrics, related to game performance. In this test post buffer time, ready time and latency time is collected. See GraphicBufferMetrics.java for explanation. Test: Manually on different devices. Bug: 116859584 Change-Id: I85eae038363860287b796c593b5192f74231cb72 (cherry picked from commit 20045a43bcf8c8bbbbbb677b4f5d49ebe4ab63f6) --- tests/GamePerformance/Android.mk | 39 ++ tests/GamePerformance/AndroidManifest.xml | 40 ++ tests/GamePerformance/res/values/themes.xml | 25 + .../android/gameperformance/ATraceRunner.java | 94 ++++ .../gameperformance/CustomOpenGLView.java | 91 +++ .../gameperformance/CustomSurfaceView.java | 190 +++++++ .../GamePerformanceActivity.java | 132 +++++ .../gameperformance/GamePerformanceTest.java | 87 +++ .../gameperformance/GraphicBufferMetrics.java | 530 ++++++++++++++++++ .../src/android/gameperformance/Utils.java | 31 + 10 files changed, 1259 insertions(+) create mode 100644 tests/GamePerformance/Android.mk create mode 100644 tests/GamePerformance/AndroidManifest.xml create mode 100644 tests/GamePerformance/res/values/themes.xml create mode 100644 tests/GamePerformance/src/android/gameperformance/ATraceRunner.java create mode 100644 tests/GamePerformance/src/android/gameperformance/CustomOpenGLView.java create mode 100644 tests/GamePerformance/src/android/gameperformance/CustomSurfaceView.java create mode 100644 tests/GamePerformance/src/android/gameperformance/GamePerformanceActivity.java create mode 100644 tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java create mode 100644 tests/GamePerformance/src/android/gameperformance/GraphicBufferMetrics.java create mode 100644 tests/GamePerformance/src/android/gameperformance/Utils.java diff --git a/tests/GamePerformance/Android.mk b/tests/GamePerformance/Android.mk new file mode 100644 index 000000000000..58654de34029 --- /dev/null +++ b/tests/GamePerformance/Android.mk @@ -0,0 +1,39 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +# Don't include this package in any target +LOCAL_MODULE_TAGS := tests + +LOCAL_DEX_PREOPT := false + +LOCAL_PROGUARD_ENABLED := disabled + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_STATIC_JAVA_LIBRARIES := android-support-test + +LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner + +LOCAL_PACKAGE_NAME := GamePerformance + +LOCAL_PRIVATE_PLATFORM_APIS := true + +LOCAL_CERTIFICATE := platform + + +include $(BUILD_PACKAGE) diff --git a/tests/GamePerformance/AndroidManifest.xml b/tests/GamePerformance/AndroidManifest.xml new file mode 100644 index 000000000000..b331e2c07e14 --- /dev/null +++ b/tests/GamePerformance/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/GamePerformance/res/values/themes.xml b/tests/GamePerformance/res/values/themes.xml new file mode 100644 index 000000000000..63130717fe72 --- /dev/null +++ b/tests/GamePerformance/res/values/themes.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/tests/GamePerformance/src/android/gameperformance/ATraceRunner.java b/tests/GamePerformance/src/android/gameperformance/ATraceRunner.java new file mode 100644 index 000000000000..25754fd79a72 --- /dev/null +++ b/tests/GamePerformance/src/android/gameperformance/ATraceRunner.java @@ -0,0 +1,94 @@ +/* + * 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.gameperformance; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; + +import android.app.Instrumentation; +import android.os.AsyncTask; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +/** + * Helper that runs atrace command for required duration and category. Results are read from + * the output of atrace and serialized to the provided file. We cannot use direct atrace to + * file because atrace is executed in UI automator context and analysis is done in test context. + * In last case output file is not accessible from the both contexts. + */ +public class ATraceRunner extends AsyncTask{ + private final static String TAG = "ATraceRunner"; + + // Report that atrace is done. + public interface Delegate { + public void onProcessed(boolean success); + } + + private final Instrumentation mInstrumentation; + private final String mOutput; + private final int mTimeInSeconds; + private final String mCategory; + private final Delegate mDelegate; + + public ATraceRunner(Instrumentation instrumentation, + String output, + int timeInSeconds, + String category, + Delegate delegate) { + mInstrumentation = instrumentation; + mOutput = output; + mTimeInSeconds = timeInSeconds; + mCategory = category; + mDelegate = delegate; + } + + @Override + protected Boolean doInBackground(Void... params) { + BufferedReader bufferedReader = null; + FileWriter writer = null; + try { + // Run the command. + final String cmd = "atrace -t " + mTimeInSeconds + " " + mCategory; + Log.i(TAG, "Running atrace... " + cmd); + writer = new FileWriter(mOutput); + final ParcelFileDescriptor fd = + mInstrumentation.getUiAutomation().executeShellCommand(cmd); + bufferedReader = new BufferedReader( + new InputStreamReader(new ParcelFileDescriptor.AutoCloseInputStream(fd))); + String line; + while ((line = bufferedReader.readLine()) != null) { + writer.write(line); + writer.write("\n"); + } + Log.i(TAG, "Running atrace... DONE"); + return true; + } catch (IOException e) { + Log.i(TAG, "atrace failed", e); + return false; + } finally { + Utils.closeQuietly(bufferedReader); + Utils.closeQuietly(writer); + } + } + + @Override + protected void onPostExecute(Boolean result) { + mDelegate.onProcessed(result); + } + +} diff --git a/tests/GamePerformance/src/android/gameperformance/CustomOpenGLView.java b/tests/GamePerformance/src/android/gameperformance/CustomOpenGLView.java new file mode 100644 index 000000000000..2b37280ae9b5 --- /dev/null +++ b/tests/GamePerformance/src/android/gameperformance/CustomOpenGLView.java @@ -0,0 +1,91 @@ +/* + * 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.gameperformance; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; + +public class CustomOpenGLView extends GLSurfaceView { + private Random mRandom; + private List mFrameTimes; + + public CustomOpenGLView(Context context) { + super(context); + + mRandom = new Random(); + mFrameTimes = new ArrayList(); + + setEGLContextClientVersion(2); + + setRenderer(new GLSurfaceView.Renderer() { + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + gl.glClearDepthf(1.0f); + gl.glEnable(GL10.GL_DEPTH_TEST); + gl.glDepthFunc(GL10.GL_LEQUAL); + + gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, + GL10.GL_NICEST); } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + GLES20.glViewport(0, 0, width, height); + } + + @Override + public void onDrawFrame(GL10 gl) { + GLES20.glClearColor( + mRandom.nextFloat(), mRandom.nextFloat(), mRandom.nextFloat(), 1.0f); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + synchronized (mFrameTimes) { + mFrameTimes.add(System.currentTimeMillis()); + } + } + }); + setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + } + + /** + * Resets frame times in order to calculate fps for different test pass. + */ + public void resetFrameTimes() { + synchronized (mFrameTimes) { + mFrameTimes.clear(); + } + } + + /** + * Returns current fps based on collected frame times. + */ + public double getFps() { + synchronized (mFrameTimes) { + if (mFrameTimes.size() < 2) { + return 0.0f; + } + return 1000.0 * mFrameTimes.size() / + (mFrameTimes.get(mFrameTimes.size() - 1) - mFrameTimes.get(0)); + } + } +} diff --git a/tests/GamePerformance/src/android/gameperformance/CustomSurfaceView.java b/tests/GamePerformance/src/android/gameperformance/CustomSurfaceView.java new file mode 100644 index 000000000000..56161362808c --- /dev/null +++ b/tests/GamePerformance/src/android/gameperformance/CustomSurfaceView.java @@ -0,0 +1,190 @@ +/* + * 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.gameperformance; + +import java.util.ArrayList; +import java.util.List; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Trace; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +/** + * Minimal SurfaceView that sends buffer on request. + */ +public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback { + // Tag for trace when buffer is requested. + public final static String LOCAL_REQUEST_BUFFER = "localRequestBuffer"; + // Tag for trace when buffer is posted. + public final static String LOCAL_POST_BUFFER = "localPostBuffer"; + + private final Object mSurfaceLock = new Object(); + // Keeps frame times. Used to calculate fps. + private List mFrameTimes; + // Surface to send. + private Surface mSurface; + private Handler mHandler; + + private Runnable mInvalidateSurfaceTask = new Runnable() { + @Override + public void run() { + synchronized (mSurfaceLock) { + if (mSurface == null) { + return; + } + invalidateSurface(true, true); + mHandler.post(this); + } + } + }; + + public CustomSurfaceView(Context context) { + super(context); + mFrameTimes = new ArrayList(); + getHolder().addCallback(this); + getHolder().setFormat(PixelFormat.OPAQUE); + + HandlerThread thread = new HandlerThread("SurfaceInvalidator"); + thread.start(); + mHandler = new Handler(thread.getLooper()); + } + + /** + * Resets frame times in order to calculate fps for different test pass. + */ + public void resetFrameTimes() { + synchronized (mSurfaceLock) { + mFrameTimes.clear(); + } + } + + /** + * Returns current fps based on collected frame times. + */ + public double getFps() { + synchronized (mSurfaceLock) { + if (mFrameTimes.size() < 2) { + return 0.0f; + } + return 1000.0 * mFrameTimes.size() / + (mFrameTimes.get(mFrameTimes.size() - 1) - mFrameTimes.get(0)); + } + } + + /** + * Invalidates surface. + * @param traceCalls set to true in case we need register trace calls. Not used for warm-up. + * @param drawFps perform drawing current fps on surface to have some payload on surface. + */ + public void invalidateSurface(boolean traceCalls, boolean drawFps) { + synchronized (mSurfaceLock) { + if (mSurface == null) { + throw new IllegalStateException("Surface is not ready"); + } + if (traceCalls) { + Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, LOCAL_REQUEST_BUFFER); + } + Canvas canvas = mSurface.lockCanvas(null); + if (traceCalls) { + Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); + } + + if (drawFps) { + int textSize = canvas.getHeight() / 24; + Paint paint = new Paint(); + paint.setTextSize(textSize); + paint.setColor(0xFFFF8040); + canvas.drawARGB(92, 255, 255, 255); + canvas.drawText("FPS: " + String.format("%.2f", getFps()), 10, 300, paint); + } + + if (traceCalls) { + Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, LOCAL_POST_BUFFER); + } + mSurface.unlockCanvasAndPost(canvas); + if (traceCalls) { + Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); + } + + mFrameTimes.add(System.currentTimeMillis()); + } + } + + /** + * Wait until surface is created and ready to use or return immediately if surface + * already exists. + */ + public void waitForSurfaceReady() { + synchronized (mSurfaceLock) { + if (mSurface == null) { + try { + mSurfaceLock.wait(5000); + } catch(InterruptedException e) { + e.printStackTrace(); + } + } + if (mSurface == null) + throw new IllegalStateException("Surface is not ready."); + } + } + + /** + * Waits until surface is destroyed or return immediately if surface does not exist. + */ + public void waitForSurfaceDestroyed() { + synchronized (mSurfaceLock) { + if (mSurface != null) { + try { + mSurfaceLock.wait(5000); + } catch(InterruptedException e) { + } + } + if (mSurface != null) + throw new IllegalStateException("Surface still exists."); + } + } + + + @Override + public void surfaceCreated(SurfaceHolder holder) { + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + // This method is always called at least once, after surfaceCreated. + synchronized (mSurfaceLock) { + mSurface = holder.getSurface(); + mSurfaceLock.notify(); + mHandler.post(mInvalidateSurfaceTask); + } + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + synchronized (mSurfaceLock) { + mHandler.removeCallbacks(mInvalidateSurfaceTask); + mSurface = null; + mSurfaceLock.notify(); + } + } +} diff --git a/tests/GamePerformance/src/android/gameperformance/GamePerformanceActivity.java b/tests/GamePerformance/src/android/gameperformance/GamePerformanceActivity.java new file mode 100644 index 000000000000..b0e6196b53d7 --- /dev/null +++ b/tests/GamePerformance/src/android/gameperformance/GamePerformanceActivity.java @@ -0,0 +1,132 @@ +/* + * 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.gameperformance; + +import java.util.concurrent.CountDownLatch; + +import android.app.Activity; +import android.graphics.Rect; +import android.os.Bundle; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.RelativeLayout; + +/** + * Minimal activity that holds SurfaceView or GLSurfaceView. + * call attachSurfaceView or attachOpenGLView to switch views. + */ +public class GamePerformanceActivity extends Activity { + private CustomSurfaceView mSurfaceView = null; + private CustomOpenGLView mOpenGLView = null; + private RelativeLayout mRootLayout; + + public void attachSurfaceView() throws InterruptedException { + synchronized (mRootLayout) { + if (mSurfaceView != null) { + return; + } + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + if (mOpenGLView != null) { + mRootLayout.removeView(mOpenGLView); + mOpenGLView = null; + } + mSurfaceView = new CustomSurfaceView(GamePerformanceActivity.this); + mRootLayout.addView(mSurfaceView); + latch.countDown(); + } + }); + latch.await(); + mSurfaceView.waitForSurfaceReady(); + } + } + + public void attachOpenGLView() throws InterruptedException { + synchronized (mRootLayout) { + if (mOpenGLView != null) { + return; + } + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + if (mSurfaceView != null) { + mRootLayout.removeView(mSurfaceView); + mSurfaceView = null; + } + mOpenGLView = new CustomOpenGLView(GamePerformanceActivity.this); + mRootLayout.addView(mOpenGLView); + latch.countDown(); + } + }); + latch.await(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + // To layouts in parent. First contains list of Surfaces and second + // controls. Controls stay on top. + mRootLayout = new RelativeLayout(this); + mRootLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + Rect rect = new Rect(); + getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); + + mOpenGLView = new CustomOpenGLView(this); + mRootLayout.addView(mOpenGLView); + + setContentView(mRootLayout); + } + + public void resetFrameTimes() { + if (mSurfaceView != null) { + mSurfaceView.resetFrameTimes(); + } else if (mOpenGLView != null) { + mOpenGLView.resetFrameTimes(); + } else { + throw new IllegalStateException("Nothing attached"); + } + } + + public double getFps() { + if (mSurfaceView != null) { + return mSurfaceView.getFps(); + } else if (mOpenGLView != null) { + return mOpenGLView.getFps(); + } else { + throw new IllegalStateException("Nothing attached"); + } + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + } +} \ No newline at end of file diff --git a/tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java b/tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java new file mode 100644 index 000000000000..e5de7d75886e --- /dev/null +++ b/tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java @@ -0,0 +1,87 @@ +/* + * 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.gameperformance; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import android.app.Activity; +import android.content.Context; +import android.graphics.PixelFormat; +import android.os.Build; +import android.os.Bundle; +import android.os.Trace; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +public class GamePerformanceTest extends + ActivityInstrumentationTestCase2 { + private final static String TAG = "GamePerformanceTest"; + + private final static int GRAPHIC_BUFFER_WARMUP_LOOP_CNT = 60; + + public GamePerformanceTest() { + super(GamePerformanceActivity.class); + } + + @SmallTest + public void testGraphicBufferMetrics() throws IOException, InterruptedException { + Bundle status = new Bundle(); + + for (int i = 0; i < 2; ++i) { + if (i == 0) { + getActivity().attachSurfaceView(); + } else { + getActivity().attachOpenGLView(); + } + + // Perform warm-up. + Thread.sleep(2000); + + // Once atrace is done, this one is triggered. + CountDownLatch latch = new CountDownLatch(1); + + final String passTag = i == 0 ? "surface" : "opengl"; + final String output = (new File(getInstrumentation().getContext().getFilesDir(), + "atrace_" + passTag + ".log")).getAbsolutePath(); + Log.i(TAG, "Collecting traces to " + output); + new ATraceRunner(getInstrumentation(), output, 5, "gfx", new ATraceRunner.Delegate() { + @Override + public void onProcessed(boolean success) { + latch.countDown(); + } + }).execute(); + + // Reset frame times and perform invalidation loop while atrace is running. + getActivity().resetFrameTimes(); + latch.await(); + + // Copy results. + final Map metrics = + GraphicBufferMetrics.processGraphicBufferResult(output, passTag); + for (Map.Entry metric : metrics.entrySet()) { + status.putDouble(metric.getKey(), metric.getValue()); + } + // Also record FPS. + status.putDouble(passTag + "_fps", getActivity().getFps()); + } + + getInstrumentation().sendStatus(Activity.RESULT_OK, status); + } +} diff --git a/tests/GamePerformance/src/android/gameperformance/GraphicBufferMetrics.java b/tests/GamePerformance/src/android/gameperformance/GraphicBufferMetrics.java new file mode 100644 index 000000000000..dffce1acdec3 --- /dev/null +++ b/tests/GamePerformance/src/android/gameperformance/GraphicBufferMetrics.java @@ -0,0 +1,530 @@ +/* + * 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.gameperformance; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * Utility class that performs analysis of atrace logs. This is implemented without Android + * dependencies and therefore can be used in stand-alone mode. + * Idea of this is to track atrace gfx event from graphics buffer producer/consumer. + * We analyze here from surfaceflinger + * queueBuffer - event when buffer was queued. + * acquireBuffer - event when buffer was requested for composition. + * releaseBuffer - even when buffer was released after composition. + * This also track events, issued locally + * localPostBuffer - event when buffer was posted from the local app. + * + * queueBuffer, acquireBuffer, releaseBuffer is accompanied with buffer name so we + * can track life-cycle of particular buffer. + * We don't have such information for localPostBuffer, however we can track next queueBuffer + * from surfaceflinger corresponds to previous localPostBuffer. + * + * Following results are calculated: + * post_time_[min/max/avr]_mcs - time for localPostBuffer duration. + * ready_time_[min/max/avr]_mcs - time from localPostBuffer to when buffer was acquired by + * surfaceflinger. + * latency_[min/max/avr]_mcs - time from localPostBuffer to when buffer was released by + * surfaceflinger. + * missed_frame_percents - percentage of missed frames (frames that do not have right sequence + * of events). + * + * Following is example of atrace logs from different platforms + * <...>-5237 (-----) [000] ...1 228.380392: tracing_mark_write: B|11|SurfaceView - android.gameperformance/android.gameperformance.GamePerformanceActivity#0: 2 + * surfaceflinger-5855 ( 5855) [001] ...1 169.627364: tracing_mark_write: B|24|acquireBuffer + * HwBinder:617_2-652 ( 617) [002] d..1 360262.921756: sde_evtlog: 617|sde_encoder_virt_atomic_check:855|19|0|0|0|0|0|0|0|0|0|0|0|0|0|0 + */ +public class GraphicBufferMetrics { + private final static String TAG = "GraphicBufferMetrics"; + + private final static String KEY_POST_TIME = "post_time"; + private final static String KEY_READY_TIME = "ready_time"; + private final static String KEY_LATENCY = "latency"; + private final static String SUFFIX_MIN = "min"; + private final static String SUFFIX_MAX = "max"; + private final static String SUFFIX_MEDIAN = "median"; + private final static String KEY_MISSED_FRAME_RATE = "missed_frame_percents"; + + private final static int EVENT_POST_BUFFER = 0; + private final static int EVENT_QUEUE_BUFFER = 1; + private final static int EVENT_ACQUIRE_BUFFER = 2; + private final static int EVENT_RELEASE_BUFFER = 3; + + // atrace prints this line. Used as a marker to make sure that we can parse its output. + private final static String ATRACE_HEADER = + "# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION"; + + private final static String[] KNOWN_PHRASES = new String[] { + "capturing trace... done", "TRACE:"}; + private final static List KNWON_PHRASES_LIST = Arrays.asList(KNOWN_PHRASES); + + // Type of the atrace event we can parse and analyze. + private final static String FUNCTION_TRACING_MARK_WRITE = "tracing_mark_write"; + + // Trace event we can ignore. It contains current timestamp information for the atrace output. + private final static String TRACE_EVENT_CLOCK_SYNC = "trace_event_clock_sync:"; + + // Threshold we consider test passes successfully. If we cannot collect enough amount of frames + // let fail the test. 50 is calculated 10 frames per second running for five seconds. + private final static int MINIMAL_SAMPLE_CNT_TO_PASS = 50; + + /** + * Raw event in atrace. Stored hierarchically. + */ + private static class RawEvent { + // Parent of this event or null for the root holder. + public final RawEvent mParent; + // Time of the event in mcs. + public final long mTime; + // Duration of the event in mcs. + public long mDuration; + // Name/body of the event. + public final String mName; + // Children events. + public final List mChildren; + + public RawEvent(RawEvent parent, long time, String name) { + mParent = parent; + mTime = time; + mName = name; + mDuration = -1; + mChildren = new ArrayList<>(); + } + + /** + * Recursively finds child events. + * @param path specify path to events. For example a/b. That means to find child with name + * 'a' of the current event and in this child find child with name 'b'. Path + * can consist from only one segment and that means we analyze only children of + * the current event. + * @param collector to collect found events. + */ + public void findEvents(String path, List collector) { + final int separator = path.indexOf('/'); + final String current = separator > 0 ? path.substring(0, separator) : path; + final String nextPath = separator > 0 ? path.substring(separator + 1) : null; + for (RawEvent child : mChildren) { + if (child.mName.equals(current)) { + if (nextPath != null) { + child.findEvents(nextPath, collector); + } else { + collector.add(child); + } + } + } + } + + public void dump(String prefix) { + System.err.print(prefix); + System.err.println(mTime + "[" + mDuration + "] " + mName); + for (RawEvent e : mChildren) { + e.dump(prefix + " "); + } + } + } + + /** + * Describes graphic buffer event. local post, queued, acquired, released. + */ + private static class BufferEvent { + public final int mType; + public final long mTime; + public final long mDuration; + public final String mBufferId; + + public BufferEvent(int type, long time, long duration, String bufferId) { + mType = type; + mTime = time; + mDuration = duration; + mBufferId = bufferId; + } + + @Override + public String toString() { + return "Type: " + mType + ". Time: " + mTime + + "[" + mDuration + "]. Buffer: " + mBufferId + "."; + } + } + + /** + * Returns true if given char is digit. + */ + private static boolean isDigitChar(char c) { + return (c >= '0') && (c <= '9'); + } + + /** + * Returns true if given char is digit or '.'. + */ + private static boolean isDoubleDigitChar(char c) { + return (c == '.') || isDigitChar(c); + } + + /** + * Convert timestamp string that represents double value in seconds to long time that represents + * timestamp in microseconds. + */ + private static long getTimeStamp(String timeStampStr) { + return (long)(1000000.0 * Double.parseDouble(timeStampStr)); + } + + /** + * Reads atrace log and build event model. Result is a map, where key specifies event for the + * particular thread. Value is the synthetic root RawEvent that holds all events for the + * thread. Events are stored hierarchically. + */ + private static Map buildEventModel(String fileName) throws IOException { + Map result = new HashMap<>(); + + BufferedReader bufferedReader = null; + String line = ""; + boolean headerDetected = false; + try { + bufferedReader = new BufferedReader(new FileReader(fileName)); + while ((line = bufferedReader.readLine()) != null) { + // Make sure we find comment that describes output format we can with. + headerDetected |= line.equals(ATRACE_HEADER); + // Skip comments. + if (line.startsWith("#")) { + continue; + } + // Skip known service output + if (KNWON_PHRASES_LIST.contains(line)) { + continue; + } + + if (!headerDetected) { + // We don't know the format of this line. + throw new IllegalStateException("Header was not detected"); + } + + // TASK-PID in header exists at position 12. PID position 17 should contains first + // digit of thread id after the '-'. + if (!isDigitChar(line.charAt(17)) || line.charAt(16) != '-') { + throw new IllegalStateException("Failed to parse thread id: " + line); + } + int rightIndex = line.indexOf(' ', 17); + final String threadIdStr = line.substring(17, rightIndex); + final int threadId = Integer.parseInt(threadIdStr); + + // TIMESTAMP in header exists at position 45 + // This position should point in the middle of timestamp which is ended by ':'. + int leftIndex = 45; + while (isDoubleDigitChar(line.charAt(leftIndex))) { + --leftIndex; + } + rightIndex = line.indexOf(':', 45); + + final String timeStampString = line.substring(leftIndex + 1, rightIndex); + final long timeStampMcs = getTimeStamp(timeStampString); + + // Find function name, pointed by FUNCTION. Long timestamp can shift if position + // so use end of timestamp to find the function which is ended by ':'. + leftIndex = rightIndex + 1; + while (Character.isWhitespace(line.charAt(leftIndex))) { + ++leftIndex; + } + rightIndex = line.indexOf(':', leftIndex); + final String function = line.substring(leftIndex, rightIndex); + + if (!function.equals(FUNCTION_TRACING_MARK_WRITE)) { + continue; + } + + // Rest of the line is event body. + leftIndex = rightIndex + 1; + while (Character.isWhitespace(line.charAt(leftIndex))) { + ++leftIndex; + } + + final String event = line.substring(leftIndex); + if (event.startsWith(TRACE_EVENT_CLOCK_SYNC)) { + continue; + } + + // Parse event, for example: + // B|615|SurfaceView - android.gameperformance.GamePerformanceActivity#0: 1 + // E|615 + // C|11253|operation id|2 + StringTokenizer eventTokenizer = new StringTokenizer(event, "|"); + final String eventType = eventTokenizer.nextToken(); + + // Attach root on demand. + if (!result.containsKey(threadId)) { + result.put(threadId, new RawEvent(null /* parent */, + timeStampMcs, + "#ROOT_" + threadId)); + } + + switch (eventType) { + case "B": { + // Log entry starts. + eventTokenizer.nextToken(); // PID + String eventText = eventTokenizer.nextToken(); + while (eventTokenizer.hasMoreTokens()) { + eventText += " "; + eventText += eventTokenizer.nextToken(); + } + RawEvent parent = result.get(threadId); + RawEvent current = new RawEvent(parent, timeStampMcs, eventText); + parent.mChildren.add(current); + result.put(threadId, current); + } + break; + case "E": { + // Log entry ends. + RawEvent current = result.get(threadId); + current.mDuration = timeStampMcs - current.mTime; + if (current.mParent == null) { + // Detect a tail of the previous call. Remove last child element if it + // exists once it does not belong to the root. + if (!current.mChildren.isEmpty()) { + current.mChildren.remove(current.mChildren.size() -1); + } + } else { + result.put(threadId, current.mParent); + } + } + break; + case "C": + // Counter, ignore + break; + default: + throw new IllegalStateException( + "Unrecognized trace: " + line + " # " + eventType + " # " + event); + } + } + + // Detect incomplete events and detach from the root. + Set threadIds = new TreeSet<>(); + threadIds.addAll(result.keySet()); + for (int threadId : threadIds) { + RawEvent root = result.get(threadId); + if (root.mParent == null) { + // Last trace was closed. + continue; + } + // Find the root. + while (root.mParent != null) { + root = root.mParent; + } + // Discard latest incomplete element. + root.mChildren.remove(root.mChildren.size() - 1); + result.put(threadId, root); + } + } catch (Exception e) { + throw new IOException("Failed to process " + line, e); + } finally { + Utils.closeQuietly(bufferedReader); + } + + return result; + } + + /** + * Processes provided atrace log and calculates graphics buffer metrics. + * @param fileName name of atrace log file. + * @param testTag tag to separate results for the different passes. + */ + public static Map processGraphicBufferResult( + String fileName, String testTag) throws IOException { + final Map model = buildEventModel(fileName); + + List collectorPostBuffer = new ArrayList<>(); + List collectorQueueBuffer = new ArrayList<>(); + List collectorReleaseBuffer = new ArrayList<>(); + List collectorAcquireBuffer = new ArrayList<>(); + + // Collect required events. + for (RawEvent root : model.values()) { + // Surface view + root.findEvents("localPostBuffer", collectorPostBuffer); + // OpengGL view + root.findEvents("eglSwapBuffersWithDamageKHR", collectorPostBuffer); + + root.findEvents("queueBuffer", collectorQueueBuffer); + root.findEvents("onMessageReceived/handleMessageInvalidate/latchBuffer/" + + "updateTexImage/acquireBuffer", + collectorAcquireBuffer); + // PI stack + root.findEvents( + "onMessageReceived/handleMessageRefresh/postComposition/releaseBuffer", + collectorReleaseBuffer); + // NYC stack + root.findEvents( + "onMessageReceived/handleMessageRefresh/releaseBuffer", + collectorReleaseBuffer); + } + + // Convert raw event to buffer events. + List bufferEvents = new ArrayList<>(); + for (RawEvent event : collectorPostBuffer) { + bufferEvents.add( + new BufferEvent(EVENT_POST_BUFFER, event.mTime, event.mDuration, null)); + } + toBufferEvents(EVENT_QUEUE_BUFFER, collectorQueueBuffer, bufferEvents); + toBufferEvents(EVENT_ACQUIRE_BUFFER, collectorAcquireBuffer, bufferEvents); + toBufferEvents(EVENT_RELEASE_BUFFER, collectorReleaseBuffer, bufferEvents); + + // Sort events based on time. These events are originally taken from different threads so + // order is not always preserved. + Collections.sort(bufferEvents, new Comparator() { + @Override + public int compare(BufferEvent o1, BufferEvent o2) { + if (o1.mTime < o2.mTime) { + return -1; + } if (o1.mTime > o2.mTime) { + return +1; + } else { + return 0; + } + } + }); + + // Collect samples. + List postTimes = new ArrayList<>(); + List readyTimes = new ArrayList<>(); + List latencyTimes = new ArrayList<>(); + long missedCnt = 0; + + for (int i = 0; i < bufferEvents.size(); ++i) { + if (bufferEvents.get(i).mType != EVENT_POST_BUFFER) { + continue; + } + final int queueIndex = findNextOfType(bufferEvents, i + 1, EVENT_QUEUE_BUFFER); + if (queueIndex < 0) { + break; + } + final int acquireIndex = findNextOfBufferId(bufferEvents, queueIndex + 1, + bufferEvents.get(queueIndex).mBufferId); + if (acquireIndex < 0) { + break; + } + if (bufferEvents.get(acquireIndex).mType != EVENT_ACQUIRE_BUFFER) { + // Was not actually presented. + ++missedCnt; + continue; + } + final int releaseIndex = findNextOfBufferId(bufferEvents, acquireIndex + 1, + bufferEvents.get(queueIndex).mBufferId); + if (releaseIndex < 0) { + break; + } + if (bufferEvents.get(releaseIndex).mType != EVENT_RELEASE_BUFFER) { + // Was not actually presented. + ++missedCnt; + continue; + } + + postTimes.add(bufferEvents.get(i).mDuration); + readyTimes.add( + bufferEvents.get(acquireIndex).mTime - bufferEvents.get(i).mTime); + latencyTimes.add( + bufferEvents.get(releaseIndex).mTime - bufferEvents.get(i).mTime); + } + + if (postTimes.size() < MINIMAL_SAMPLE_CNT_TO_PASS) { + throw new IllegalStateException("Too few sample cnt: " + postTimes.size() +". " + + MINIMAL_SAMPLE_CNT_TO_PASS + " is required."); + } + + Map status = new TreeMap<>(); + addResults(status, testTag, KEY_POST_TIME, postTimes); + addResults(status, testTag, KEY_READY_TIME, readyTimes); + addResults(status, testTag, KEY_LATENCY, latencyTimes); + status.put(testTag + "_" + KEY_MISSED_FRAME_RATE, + 100.0 * missedCnt / (missedCnt + postTimes.size())); + return status; + } + + private static void addResults( + Map status, String tag, String key, List times) { + Collections.sort(times); + long min = times.get(0); + long max = times.get(0); + for (long time : times) { + min = Math.min(min, time); + max = Math.max(max, time); + } + status.put(tag + "_" + key + "_" + SUFFIX_MIN, (double)min); + status.put(tag + "_" + key + "_" + SUFFIX_MAX, (double)max); + status.put(tag + "_" + key + "_" + SUFFIX_MEDIAN, (double)times.get(times.size() / 2)); + } + + // Helper to convert surface flinger events to buffer events. + private static void toBufferEvents( + int type, List rawEvents, List bufferEvents) { + for (RawEvent event : rawEvents) { + if (event.mChildren.isEmpty()) { + throw new IllegalStateException("Buffer name is expected"); + } + final String bufferName = event.mChildren.get(0).mName; + if (bufferName.startsWith("SurfaceView - android.gameperformance")) { + bufferEvents.add( + new BufferEvent(type, event.mTime, event.mDuration, bufferName)); + } + } + } + + private static int findNextOfType(List events, int startIndex, int type) { + for (int i = startIndex; i < events.size(); ++i) { + if (events.get(i).mType == type) { + return i; + } + } + return -1; + } + + private static int findNextOfBufferId( + List events, int startIndex, String bufferId) { + for (int i = startIndex; i < events.size(); ++i) { + if (bufferId.equals(events.get(i).mBufferId)) { + return i; + } + } + return -1; + } + + public static void main(String[] args) { + if (args.length != 1) { + System.err.println("Usage: " + TAG + " atrace.log"); + return; + } + + try { + System.out.println("Results:"); + for (Map.Entry entry : + processGraphicBufferResult(args[0], "default").entrySet()) { + System.out.println(" " + entry.getKey() + " = " + entry.getValue()); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/tests/GamePerformance/src/android/gameperformance/Utils.java b/tests/GamePerformance/src/android/gameperformance/Utils.java new file mode 100644 index 000000000000..64819712bf6d --- /dev/null +++ b/tests/GamePerformance/src/android/gameperformance/Utils.java @@ -0,0 +1,31 @@ +/* + * 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.gameperformance; + +import java.io.Closeable; +import java.io.IOException; + +public class Utils { + public static void closeQuietly(Closeable closeable) { + try { + if (closeable != null) { + closeable.close(); + } + } catch (IOException e) { + // Ignore + } + } +} -- GitLab From b244607219e322504298645db7fa41b34d26eb68 Mon Sep 17 00:00:00 2001 From: WyattRiley Date: Fri, 1 Mar 2019 07:41:49 -0800 Subject: [PATCH 003/450] Clarifying Battery Saver GPS Disable Rename variable, and re-order complex enable logic for readability. Test: Build and works with settings on device. Change-Id: I67e6eca892e2180a4861bd395a2d7f7b93ecb6ba --- .../server/location/GnssLocationProvider.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index ae915037947e..22cc2cfb7f76 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -351,8 +351,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // The WorkSource associated with the most recent client request (i.e, most recent call to // setRequest). private WorkSource mWorkSource = null; - // True if gps should be disabled (used to support battery saver mode in settings). - private boolean mDisableGps = false; + // True if gps should be disabled because of PowerManager controls + private boolean mDisableGpsForPowerManager = false; /** * Properties loaded from PROPERTIES_FILE. @@ -525,18 +525,19 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private void updateLowPowerMode() { // Disable GPS if we are in device idle mode. - boolean disableGps = mPowerManager.isDeviceIdleMode(); + boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode(); final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION); switch (result.locationMode) { case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF: case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF: // If we are in battery saver mode and the screen is off, disable GPS. - disableGps |= result.batterySaverEnabled && !mPowerManager.isInteractive(); + disableGpsForPowerManager |= + result.batterySaverEnabled && !mPowerManager.isInteractive(); break; } - if (disableGps != mDisableGps) { - mDisableGps = disableGps; + if (disableGpsForPowerManager != mDisableGpsForPowerManager) { + mDisableGpsForPowerManager = disableGpsForPowerManager; updateEnabled(); updateRequirements(); } @@ -933,11 +934,19 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private void updateEnabled() { synchronized (mLock) { - boolean enabled = - ((mProviderRequest != null && mProviderRequest.reportLocation - && mProviderRequest.locationSettingsIgnored) || ( - mContext.getSystemService(LocationManager.class).isLocationEnabled() - && !mDisableGps)) && !mShutdown; + // Generally follow location setting + boolean enabled = mContext.getSystemService(LocationManager.class).isLocationEnabled(); + + // ... but disable if PowerManager overrides + enabled &= !mDisableGpsForPowerManager; + + // .. but enable anyway, if there's an active settings-ignored request (e.g. ELS) + enabled |= (mProviderRequest != null && mProviderRequest.reportLocation + && mProviderRequest.locationSettingsIgnored); + + // ... and, finally, disable anyway, if device is being shut down + enabled &= !mShutdown; + if (enabled == mEnabled) { return; } @@ -2118,7 +2127,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements .append(mGnssMeasurementsProvider.isRegistered()).append('\n'); s.append(" mGnssNavigationMessageProvider.isRegistered()=") .append(mGnssNavigationMessageProvider.isRegistered()).append('\n'); - s.append(" mDisableGps (battery saver mode)=").append(mDisableGps).append('\n'); + s.append(" mDisableGpsForPowerManager=").append(mDisableGpsForPowerManager).append('\n'); s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)); s.append(" ( "); if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHEDULING "); -- GitLab From 69de5fbb71c22ee5bcc7773c1da33452b5843edc Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Sat, 23 Feb 2019 01:19:50 +0800 Subject: [PATCH 004/450] Use inner frame of letterbox as touchable region for letterboxed app Since commit 192fe76a, the letterbox is touchable. In order for the touch event to be able to slip into the activity, the touchable region of activity must not cover the letterbox. Also toggle the token of window handle with surface show/hide to reduce the inactive window records in InputDispatcher. Bug: 110257889 Test: manual - Enable display cutout, open DocumentUI in landscape and drag from the black region in the cutout side to DocumentUI, the list view should scroll or the drawer slides out. Change-Id: I4a0bffebfe7a3efcac842c0abbae60ea2a3487bd --- .../com/android/server/wm/AppWindowToken.java | 11 +++++++- .../java/com/android/server/wm/Letterbox.java | 27 ++++++++++++++----- .../com/android/server/wm/WindowState.java | 26 +++++++++++------- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 220370c0859c..2367ccce38e4 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -3132,8 +3132,17 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */ + void getLetterboxInnerBounds(Rect outBounds) { + if (mLetterbox != null) { + outBounds.set(mLetterbox.getInnerFrame()); + } else { + outBounds.setEmpty(); + } + } + /** - * @eturn true if there is a letterbox and any part of that letterbox overlaps with + * @return {@code true} if there is a letterbox and any part of that letterbox overlaps with * the given {@code rect}. */ boolean isLetterboxOverlappingWith(Rect rect) { diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index 3110fb9a40fe..c3ea72f4c2fe 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -92,6 +92,11 @@ public class Letterbox { mBottom.getHeight()); } + /** @return The frame that used to place the content. */ + Rect getInnerFrame() { + return mInner; + } + /** * Returns true if any part of the letterbox overlaps with the given {@code rect}. */ @@ -162,6 +167,7 @@ public class Letterbox { final InputWindowHandle mWindowHandle; final InputEventReceiver mInputEventReceiver; final WindowManagerService mWmService; + final Binder mToken = new Binder(); InputInterceptor(String namePrefix, WindowState win) { mWmService = win.mWmService; @@ -171,13 +177,12 @@ public class Letterbox { mClientChannel = channels[1]; mInputEventReceiver = new SimpleInputReceiver(mClientChannel); - final Binder token = new Binder(); - mWmService.mInputManager.registerInputChannel(mServerChannel, token); + mWmService.mInputManager.registerInputChannel(mServerChannel, mToken); mWindowHandle = new InputWindowHandle(null /* inputApplicationHandle */, null /* clientWindow */, win.getDisplayId()); mWindowHandle.name = name; - mWindowHandle.token = token; + mWindowHandle.token = mToken; mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH @@ -192,6 +197,14 @@ public class Letterbox { } void updateTouchableRegion(Rect frame) { + if (frame.isEmpty()) { + // Use null token to indicate the surface doesn't need to receive input event (see + // the usage of Layer.hasInput in SurfaceFlinger), so InputDispatcher won't keep the + // unnecessary records. + mWindowHandle.token = null; + return; + } + mWindowHandle.token = mToken; mWindowHandle.touchableRegion.set(frame); mWindowHandle.touchableRegion.translate(-frame.left, -frame.top); } @@ -289,14 +302,14 @@ public class Letterbox { t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top); t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(), mSurfaceFrameRelative.height()); - if (mInputInterceptor != null) { - mInputInterceptor.updateTouchableRegion(mSurfaceFrameRelative); - t.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle); - } t.show(mSurface); } else if (mSurface != null) { t.hide(mSurface); } + if (mSurface != null && mInputInterceptor != null) { + mInputInterceptor.updateTouchableRegion(mSurfaceFrameRelative); + t.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle); + } } public boolean needsApplySurfaceChanges() { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 20cca66d8a52..c31764ae55cf 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2180,16 +2180,22 @@ class WindowState extends WindowContainer implements WindowManagerP if (modal && mAppToken != null) { // Limit the outer touch to the activity stack region. flags |= FLAG_NOT_TOUCH_MODAL; - // If this is a modal window we need to dismiss it if it's not full screen and the - // touch happens outside of the frame that displays the content. This means we - // need to intercept touches outside of that window. The dim layer user - // associated with the window (task or stack) will give us the good bounds, as - // they would be used to display the dim layer. - final Task task = getTask(); - if (task != null) { - task.getDimBounds(mTmpRect); - } else { - getStack().getDimBounds(mTmpRect); + // If the inner bounds of letterbox is available, then it will be used as the touchable + // region so it won't cover the touchable letterbox and the touch events can slip to + // activity from letterbox. + mAppToken.getLetterboxInnerBounds(mTmpRect); + if (mTmpRect.isEmpty()) { + // If this is a modal window we need to dismiss it if it's not full screen and the + // touch happens outside of the frame that displays the content. This means we need + // to intercept touches outside of that window. The dim layer user associated with + // the window (task or stack) will give us the good bounds, as they would be used to + // display the dim layer. + final Task task = getTask(); + if (task != null) { + task.getDimBounds(mTmpRect); + } else { + getStack().getDimBounds(mTmpRect); + } } if (inFreeformWindowingMode()) { // For freeform windows we the touch region to include the whole surface for the -- GitLab From 80e1e5c94393a39e1977d3c155a61d10ab9c8104 Mon Sep 17 00:00:00 2001 From: Malcolm Chen Date: Wed, 27 Feb 2019 15:16:05 -0800 Subject: [PATCH 005/450] Adding nullability annotation. For below APIs: removeOnOpportunisticSubscriptionChangedListener switchToSubscription Bug: 126391202 Test: build Change-Id: I791fd6307371ef9a1918b119f4a1b15623517157 Merged-In: I791fd6307371ef9a1918b119f4a1b15623517157 --- api/current.txt | 4 ++-- telephony/java/android/telephony/SubscriptionManager.java | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/api/current.txt b/api/current.txt index 482a221ee80d..6cdcd3639d2f 100755 --- a/api/current.txt +++ b/api/current.txt @@ -42959,7 +42959,7 @@ package android.telephony { method public boolean isNetworkRoaming(int); method public static boolean isUsableSubscriptionId(int); method public static boolean isValidSubscriptionId(int); - method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); + method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean removeSubscriptionsFromGroup(@NonNull int[]); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setMetered(boolean, int); @@ -42968,7 +42968,7 @@ package android.telephony { method public void setSubscriptionOverrideCongested(int, boolean, long); method public void setSubscriptionOverrideUnmetered(int, boolean, long); method public void setSubscriptionPlans(int, @NonNull java.util.List); - method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, android.app.PendingIntent); + method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, @NonNull android.app.PendingIntent); field public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED"; field public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED"; field public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS"; diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 1301246f8574..8ee4fd0b9c6a 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -63,6 +63,7 @@ import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.PhoneConstants; +import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -1073,7 +1074,8 @@ public class SubscriptionManager { * @param listener that is to be unregistered. */ public void removeOnOpportunisticSubscriptionsChangedListener( - OnOpportunisticSubscriptionsChangedListener listener) { + @NonNull OnOpportunisticSubscriptionsChangedListener listener) { + Preconditions.checkNotNull(listener, "listener cannot be null"); String pkgForDebug = mContext != null ? mContext.getOpPackageName() : ""; if (DBG) { logd("unregister OnOpportunisticSubscriptionsChangedListener pkgForDebug=" @@ -2689,7 +2691,8 @@ public class SubscriptionManager { * @param callbackIntent pending intent that will be sent after operation is done. */ @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) - public void switchToSubscription(int subId, PendingIntent callbackIntent) { + public void switchToSubscription(int subId, @NonNull PendingIntent callbackIntent) { + Preconditions.checkNotNull(callbackIntent, "callbackIntent cannot be null"); EuiccManager euiccManager = new EuiccManager(mContext); euiccManager.switchToSubscription(subId, callbackIntent); } -- GitLab From 593d7ebaed6fda8dc2729f0394feacb1ca0b69f5 Mon Sep 17 00:00:00 2001 From: Malcolm Chen Date: Tue, 26 Feb 2019 19:42:56 -0800 Subject: [PATCH 006/450] Add @Nullable on SubscriptionInfo#getGroupUuid SubscriptionInfo#getGroupUuid will return null if group is not set. Bug: 123587615 Test: build Change-Id: I592d35388d88bda1f908441824772fea36d43059 Merged-In: I592d35388d88bda1f908441824772fea36d43059 --- api/current.txt | 2 +- telephony/java/android/telephony/SubscriptionInfo.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/current.txt b/api/current.txt index 6cdcd3639d2f..d2b81b093e3f 100755 --- a/api/current.txt +++ b/api/current.txt @@ -42918,7 +42918,7 @@ package android.telephony { method public String getCountryIso(); method public int getDataRoaming(); method public CharSequence getDisplayName(); - method public String getGroupUuid(); + method @Nullable public String getGroupUuid(); method public String getIccId(); method public int getIconTint(); method @Deprecated public int getMcc(); diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 58f12e2c427a..b781b109bdcc 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -467,7 +467,7 @@ public class SubscriptionInfo implements Parcelable { * @return group UUID a String of group UUID if it belongs to a group. Otherwise * it will return null. */ - public String getGroupUuid() { + public @Nullable String getGroupUuid() { return mGroupUUID; } -- GitLab From e5a6e44486769052edfdbb298495a01235b07a65 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Mon, 11 Mar 2019 14:45:07 +0100 Subject: [PATCH 007/450] Start a filesystem checkpoint when committing staged install. When submitting a staged session for installation, start a filesystem checkpoint as well. This is a no-op on devices that don't support filesystem checkpointing. Bug: 126740531 Test: builds, manually inspect checkpoint state on subsequent boots Change-Id: I7f73ece143926654628417d0c2384c3d0c1ef754 --- .../com/android/server/pm/StagingManager.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 22a85eb20206..206c806013e3 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -42,11 +42,13 @@ import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.storage.IStorageManager; import android.util.Slog; import android.util.SparseArray; import android.util.apk.ApkSignatureVerifier; import com.android.internal.annotations.GuardedBy; +import com.android.internal.content.PackageHelper; import com.android.internal.os.BackgroundThread; import java.io.File; @@ -252,6 +254,21 @@ public class StagingManager { } } + // Make sure we start a filesystem checkpoint on the next boot. + try { + IStorageManager storageManager = PackageHelper.getStorageManager(); + if (storageManager.supportsCheckpoint()) { + storageManager.startCheckpoint(1 /* numRetries */); + } + } catch (RemoteException e) { + // While StorageManager lives in the same process, the native implementation + // it calls through lives in 'vold'; so, this call can fail if 'vold' isn't + // reachable. + // Since we can live without filesystem checkpointing, just warn in this case + // and continue. + Slog.w(TAG, "Could not start filesystem checkpoint."); + } + session.setStagedSessionReady(); if (sessionContainsApex(session) && !mApexManager.markStagedSessionReady(session.sessionId)) { -- GitLab From 5e904c3d01c6249307b4f85270b575de54d77c3b Mon Sep 17 00:00:00 2001 From: Fabien Sanglard Date: Wed, 13 Mar 2019 15:09:46 -0700 Subject: [PATCH 008/450] AAPT2: Delete dead code Test: None Bug: None Change-Id: Ie8e32e47883ce5e4014e9e902c22aed68af450c4 --- tools/aapt2/ResourceTable.cpp | 41 ----------------------------------- tools/aapt2/ResourceTable.h | 19 ---------------- 2 files changed, 60 deletions(-) diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index dbd0a0ca1799..6983314d6a9d 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -355,37 +355,6 @@ bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const Resourc (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag); } -bool ResourceTable::AddFileReference(const ResourceNameRef& name, - const ConfigDescription& config, - const Source& source, - const StringPiece& path, - IDiagnostics* diag) { - return AddFileReferenceImpl(name, config, source, path, nullptr, - (validate_resources_ ? ResourceNameValidator : SkipNameValidator), - diag); -} - -bool ResourceTable::AddFileReferenceMangled(const ResourceNameRef& name, - const ConfigDescription& config, const Source& source, - const StringPiece& path, io::IFile* file, - IDiagnostics* diag) { - return AddFileReferenceImpl(name, config, source, path, file, - (validate_resources_ ? ResourceNameValidator : SkipNameValidator), - diag); -} - -bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name, - const ConfigDescription& config, const Source& source, - const StringPiece& path, io::IFile* file, - NameValidator name_validator, IDiagnostics* diag) { - std::unique_ptr fileRef = - util::make_unique(string_pool.MakeRef(path)); - fileRef->SetSource(source); - fileRef->file = file; - return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef), - name_validator, ResolveValueCollision, diag); -} - bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config, const StringPiece& product, std::unique_ptr value, IDiagnostics* diag) { @@ -510,11 +479,6 @@ bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& return SetVisibilityImpl(name, visibility, {}, ResourceNameValidator, diag); } -bool ResourceTable::SetVisibilityMangled(const ResourceNameRef& name, const Visibility& visibility, - IDiagnostics* diag) { - return SetVisibilityImpl(name, visibility, {}, SkipNameValidator, diag); -} - bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility, const ResourceId& res_id, IDiagnostics* diag) { return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag); @@ -632,11 +596,6 @@ bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayabl return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag); } -bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name, - const OverlayableItem& overlayable, IDiagnostics* diag) { - return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag); -} - bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const OverlayableItem& overlayable, NameValidator name_validator, IDiagnostics *diag) { diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 32dfd260e53c..7ed78975a01d 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -239,13 +239,6 @@ class ResourceTable { const android::StringPiece& product, std::unique_ptr value, IDiagnostics* diag); - bool AddFileReference(const ResourceNameRef& name, const android::ConfigDescription& config, - const Source& source, const android::StringPiece& path, IDiagnostics* diag); - - bool AddFileReferenceMangled(const ResourceNameRef& name, const android::ConfigDescription& config, - const Source& source, const android::StringPiece& path, - io::IFile* file, IDiagnostics* diag); - // Same as AddResource, but doesn't verify the validity of the name. This is used // when loading resources from an existing binary resource table that may have mangled names. bool AddResourceMangled(const ResourceNameRef& name, const android::ConfigDescription& config, @@ -260,8 +253,6 @@ class ResourceTable { bool GetValidateResources(); bool SetVisibility(const ResourceNameRef& name, const Visibility& visibility, IDiagnostics* diag); - bool SetVisibilityMangled(const ResourceNameRef& name, const Visibility& visibility, - IDiagnostics* diag); bool SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility, const ResourceId& res_id, IDiagnostics* diag); bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility, @@ -269,8 +260,6 @@ class ResourceTable { bool SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable, IDiagnostics *diag); - bool SetOverlayableMangled(const ResourceNameRef& name, const OverlayableItem& overlayable, - IDiagnostics* diag); bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag); bool SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new, @@ -333,10 +322,6 @@ class ResourceTable { NameValidator name_validator, const CollisionResolverFunc& conflict_resolver, IDiagnostics* diag); - bool AddFileReferenceImpl(const ResourceNameRef& name, const android::ConfigDescription& config, - const Source& source, const android::StringPiece& path, io::IFile* file, - NameValidator name_validator, IDiagnostics* diag); - bool SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility, const ResourceId& res_id, NameValidator name_validator, IDiagnostics* diag); @@ -347,10 +332,6 @@ class ResourceTable { bool SetOverlayableImpl(const ResourceNameRef &name, const OverlayableItem& overlayable, NameValidator name_validator, IDiagnostics *diag); - bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id, - const Visibility& symbol, NameValidator name_validator, - IDiagnostics* diag); - // Controls whether the table validates resource names and prevents duplicate resource names bool validate_resources_ = true; -- GitLab From 16dfa6faa6eb4790665e422abd93e578eb60a1ed Mon Sep 17 00:00:00 2001 From: Alex Kershaw Date: Mon, 4 Mar 2019 12:30:58 +0000 Subject: [PATCH 009/450] Add @NonNull to parameter. Add @NonNull to second parameter of InstallSystemUpdateCallback. Fixes: 126701431 Test: Manual Change-Id: I5ced921b2c925e3318c564707fb2f83dfa31a386 --- api/current.txt | 2 +- core/java/android/app/admin/DevicePolicyManager.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/current.txt b/api/current.txt index d1b77f2c1096..c406e230060e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6964,7 +6964,7 @@ package android.app.admin { public abstract static class DevicePolicyManager.InstallSystemUpdateCallback { ctor public DevicePolicyManager.InstallSystemUpdateCallback(); - method public void onInstallUpdateError(int, String); + method public void onInstallUpdateError(int, @NonNull String); field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5 field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4 field public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2; // 0x2 diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 9a4e2151dd3b..5d7e7793da2c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2165,7 +2165,7 @@ public class DevicePolicyManager { * reported back to the IT admin to be read. */ public void onInstallUpdateError( - @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) { + @InstallUpdateCallbackErrorConstants int errorCode, @NonNull String errorMessage) { } } -- GitLab From e6d280d660dcd5563a4f7ac1417c0de3fd9ef6be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 21 Mar 2018 16:08:33 +0100 Subject: [PATCH 010/450] Use AudioProductStrategies within AudioAttributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AudioProductStrategies allows to avoid any hard coded operation in AOSP, and the matching between the stream type and the attributes can also be exposed by the XML audio policy engine configuration file. This CL updates the AudioAttributes to make use of product strategies to implement the legacy conversion between stream type and attributes. It will continue fallbacking in hard coded mapping. Bug: 124767636 Test: dumpsys media.audio_policy Signed-off-by: François Gaffie Change-Id: I143403a81e363ed064cb8b422e5ca53cb4b6e18b --- media/java/android/media/AudioAttributes.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index c2f29bc6e3bc..5249b10f9b2e 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; +import android.media.audiopolicy.AudioProductStrategies; import android.os.Build; import android.os.Bundle; import android.os.Parcel; @@ -725,6 +726,13 @@ public final class AudioAttributes implements Parcelable { */ @UnsupportedAppUsage public Builder setInternalLegacyStreamType(int streamType) { + final AudioProductStrategies ps = new AudioProductStrategies(); + if (ps.size() > 0) { + AudioAttributes attributes = ps.getAudioAttributesForLegacyStreamType(streamType); + if (attributes != null) { + return new Builder(attributes); + } + } switch(streamType) { case AudioSystem.STREAM_VOICE_CALL: mContentType = CONTENT_TYPE_SPEECH; @@ -1100,6 +1108,10 @@ public final class AudioAttributes implements Parcelable { AudioSystem.STREAM_MUSIC : AudioSystem.STREAM_TTS; } + final AudioProductStrategies ps = new AudioProductStrategies(); + if (ps.size() > 0) { + return ps.getLegacyStreamTypeForAudioAttributes(aa); + } // usage to stream type mapping switch (aa.getUsage()) { case USAGE_MEDIA: -- GitLab From e220d3b5c4c8c6a4e2c794f5a9c28fec16e89955 Mon Sep 17 00:00:00 2001 From: David Brageul Date: Mon, 24 Sep 2018 08:40:49 +0200 Subject: [PATCH 011/450] Get volume indexes from Audio Policy Service This patch reads min/max volume indexes from Audio Policy Service. Bug: 124767636 Test: dumpsys media.audio_policy Change-Id: Ic473721a856c37e95104d2fc24f5f8ca942b5335 Signed-off-by: David Brageul --- .../android/server/audio/AudioService.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 68f76abe829a..56aa15bdd22d 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -637,6 +637,26 @@ public class AudioService extends IAudioService.Stub sAudioVolumeGroups = new AudioVolumeGroups(); // Initialize volume + // Priority 1 - Android Property + // Priority 2 - Audio Policy Service + // Priority 3 - Default Value + if (sAudioProductStrategies.size() > 0) { + int numStreamTypes = AudioSystem.getNumStreamTypes(); + + for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { + AudioAttributes attr = + sAudioProductStrategies.getAudioAttributesForLegacyStreamType(streamType); + int maxVolume = AudioSystem.getMaxVolumeIndexForAttributes(attr); + if (maxVolume != -1) { + MAX_STREAM_VOLUME[streamType] = maxVolume; + } + int minVolume = AudioSystem.getMinVolumeIndexForAttributes(attr); + if (minVolume != -1) { + MIN_STREAM_VOLUME[streamType] = minVolume; + } + } + } + int maxCallVolume = SystemProperties.getInt("ro.config.vc_call_vol_steps", -1); if (maxCallVolume != -1) { MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = maxCallVolume; @@ -1465,6 +1485,11 @@ public class AudioService extends IAudioService.Stub } private int rescaleIndex(int index, int srcStream, int dstStream) { + int max = mStreamStates[srcStream].getMaxIndex(); + if (max == 0) { + Log.e(TAG, "rescaleIndex : Max index should not be zero"); + return mStreamStates[srcStream].getMinIndex(); + } final int rescaled = (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) -- GitLab From bfbe71e1044fa3d49834bf25a34c3d8b3b4bf7b8 Mon Sep 17 00:00:00 2001 From: Michael Wachenschwanz Date: Tue, 12 Mar 2019 13:14:58 -0700 Subject: [PATCH 012/450] Minor fixes to IntervalStats Close hole in IntervalStats time tracking. Fix EventTracker commitTime math Also make eventList final Fixes: 124850206 Test: builds and runs Change-Id: I967cd4b3f5ccea0f7f8fd7e8729f258a5f8f1981 --- core/java/android/app/usage/EventList.java | 14 ++++++++++++++ .../com/android/server/usage/IntervalStats.java | 16 +++++++++++----- .../android/server/usage/UsageStatsDatabase.java | 3 ++- .../server/usage/UserUsageStatsService.java | 2 +- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java index aaae57e526a0..8c0340585573 100644 --- a/core/java/android/app/usage/EventList.java +++ b/core/java/android/app/usage/EventList.java @@ -103,4 +103,18 @@ public class EventList { } return result; } + + /** + * Merge the {@link UsageEvents.Event events} in the given {@link EventList list} into this + * list while keeping the list sorted based on the event {@link + * UsageEvents.Event#mTimeStamp timestamps}. + * + * @param events The event list to merge + */ + public void merge(EventList events) { + final int size = events.size(); + for (int i = 0; i < size; i++) { + insert(events.get(i)); + } + } } diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index 8feed7fdb785..a783a4007da1 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -66,7 +66,7 @@ public class IntervalStats { public final ArrayMap packageStats = new ArrayMap<>(); public final ArrayMap configurations = new ArrayMap<>(); public Configuration activeConfiguration; - public EventList events = new EventList(); + public final EventList events = new EventList(); // A string cache. This is important as when we're parsing XML files, we don't want to // keep hundreds of strings that have the same contents. We will read the string @@ -82,7 +82,7 @@ public class IntervalStats { public void commitTime(long timeStamp) { if (curStartTime != 0) { - duration += timeStamp - duration; + duration += timeStamp - curStartTime; curStartTime = 0; } } @@ -305,7 +305,9 @@ public class IntervalStats { UsageStats usageStats = getOrCreateUsageStats(packageName); usageStats.update(className, timeStamp, eventType, instanceId); } - endTime = timeStamp; + if (timeStamp > endTime) { + endTime = timeStamp; + } } /** @@ -328,6 +330,9 @@ public class IntervalStats { event.mNotificationChannelId = getCachedStringRef(event.mNotificationChannelId); } events.insert(event); + if (event.mTimeStamp > endTime) { + endTime = event.mTimeStamp; + } } void updateChooserCounts(String packageName, String category, String action) { @@ -360,8 +365,9 @@ public class IntervalStats { configStats.mActivationCount += 1; activeConfiguration = configStats.mConfiguration; } - - endTime = timeStamp; + if (timeStamp > endTime) { + endTime = timeStamp; + } } void incrementAppLaunchCount(String packageName) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index 485a79d63862..c55bb3cf0ffe 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -1166,7 +1166,8 @@ public class UsageStatsDatabase { if (beingRestored == null) return null; beingRestored.activeConfiguration = onDevice.activeConfiguration; beingRestored.configurations.putAll(onDevice.configurations); - beingRestored.events = onDevice.events; + beingRestored.events.clear(); + beingRestored.events.merge(onDevice.events); return beingRestored; } diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 9498e161ff61..26bfcc944400 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -334,7 +334,7 @@ class UserUsageStatsService { final IntervalStats diskStats = mDatabase.getLatestUsageStats( INTERVAL_DAILY); StringBuilder sb = new StringBuilder(256); - sb.append("Last 24 hours of UsageStats missing! timeRange : "); + sb.append("Recent UsageStats missing! timeRange : "); sb.append(beginTime); sb.append(", "); sb.append(endTime); -- GitLab From 390f4c03ab15a0543aa3b07bb566571e9394d45a Mon Sep 17 00:00:00 2001 From: KOUSHIK PANUGANTI Date: Wed, 13 Mar 2019 16:53:33 -0700 Subject: [PATCH 013/450] Migrate frameworks/base/packages/Shell to androidx.test See go/jetpack-test-android-migration Test: mmma frameworks/base/packages/Shell Change-Id: I26076b3ad1871dde3ef6b206bf5502648d1a0cbb --- packages/Shell/tests/Android.mk | 2 +- packages/Shell/tests/AndroidManifest.xml | 2 +- packages/Shell/tests/AndroidTest.xml | 2 +- .../shell/BugreportProgressServiceTest.java | 7 +-- .../android/shell/BugreportReceiverTest.java | 62 +++++++++---------- 5 files changed, 37 insertions(+), 38 deletions(-) diff --git a/packages/Shell/tests/Android.mk b/packages/Shell/tests/Android.mk index b93ddde16e4a..44ff3383d566 100644 --- a/packages/Shell/tests/Android.mk +++ b/packages/Shell/tests/Android.mk @@ -9,7 +9,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-test \ + androidx.test.rules \ mockito-target-minus-junit4 \ ub-uiautomator \ junit \ diff --git a/packages/Shell/tests/AndroidManifest.xml b/packages/Shell/tests/AndroidManifest.xml index 6d564c640fcd..e845ef95b28e 100644 --- a/packages/Shell/tests/AndroidManifest.xml +++ b/packages/Shell/tests/AndroidManifest.xml @@ -36,7 +36,7 @@ - diff --git a/packages/Shell/tests/AndroidTest.xml b/packages/Shell/tests/AndroidTest.xml index e592d82bfaf7..e03b68a03d0e 100644 --- a/packages/Shell/tests/AndroidTest.xml +++ b/packages/Shell/tests/AndroidTest.xml @@ -22,7 +22,7 @@