Merge changes Icc5b8563,I4298502e,If8b715f4,If46d741e,I702d547d, ...
* changes:
Build native EventLog and XmlBlock for host linux.
Build native AssetManager and friends for host linux.
Build and potentially register Trace for host.
Build and register native MessageQueue for host.
Build native Log for host.
Build and register SystemProperties for host
Compile SystemClock.cpp for host.
diff --git a/Android.bp b/Android.bp
index 1902a5f..727842e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -757,6 +757,7 @@
static_libs: [
"apex_aidl_interface-java",
+ "suspend_control_aidl_interface-java",
"framework-protos",
"game-driver-protos",
"android.hidl.base-V1.0-java",
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 4e0a8eb..ff7416c 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -269,6 +269,7 @@
if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) {
if (currentTimestampNs - lastBroadcastTime->second <
StatsdStats::kMinActivationBroadcastPeriodNs) {
+ StatsdStats::getInstance().noteActivationBroadcastGuardrailHit(uid);
VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us.");
return;
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index ce0e561..a836bd1 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -51,6 +51,7 @@
const int FIELD_ID_SYSTEM_SERVER_RESTART = 15;
const int FIELD_ID_LOGGER_ERROR_STATS = 16;
const int FIELD_ID_OVERFLOW = 18;
+const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL = 19;
const int FIELD_ID_ATOM_STATS_TAG = 1;
const int FIELD_ID_ATOM_STATS_COUNT = 2;
@@ -109,6 +110,9 @@
const int FIELD_ID_UID_MAP_DROPPED_CHANGES = 3;
const int FIELD_ID_UID_MAP_DELETED_APPS = 4;
+const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID = 1;
+const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME = 2;
+
const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
{android::util::BINDER_CALLS, {6000, 10000}},
{android::util::LOOPER_STATS, {1500, 2500}},
@@ -236,6 +240,19 @@
vec.push_back(timeSec);
}
+void StatsdStats::noteActivationBroadcastGuardrailHit(const int uid) {
+ noteActivationBroadcastGuardrailHit(uid, getWallClockSec());
+}
+
+void StatsdStats::noteActivationBroadcastGuardrailHit(const int uid, const int32_t timeSec) {
+ lock_guard<std::mutex> lock(mLock);
+ auto& guardrailTimes = mActivationBroadcastGuardrailStats[uid];
+ if (guardrailTimes.size() == kMaxTimestampCount) {
+ guardrailTimes.pop_front();
+ }
+ guardrailTimes.push_back(timeSec);
+}
+
void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes) {
noteDataDropped(key, totalBytes, getWallClockSec());
}
@@ -590,6 +607,7 @@
pullStats.second.unregisteredCount = 0;
}
mAtomMetricStats.clear();
+ mActivationBroadcastGuardrailStats.clear();
}
string buildTimeString(int64_t timeSec) {
@@ -758,6 +776,17 @@
dprintf(out, "Event queue overflow: %d; MaxHistoryNs: %lld; MinHistoryNs: %lld\n",
mOverflowCount, (long long)mMaxQueueHistoryNs, (long long)mMinQueueHistoryNs);
+
+ if (mActivationBroadcastGuardrailStats.size() > 0) {
+ dprintf(out, "********mActivationBroadcastGuardrail stats***********\n");
+ for (const auto& pair: mActivationBroadcastGuardrailStats) {
+ dprintf(out, "Uid %d: Times: ", pair.first);
+ for (const auto& guardrailHitTime : pair.second) {
+ dprintf(out, "%d ", guardrailHitTime);
+ }
+ }
+ dprintf(out, "\n");
+ }
}
void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* proto) {
@@ -959,6 +988,20 @@
restart);
}
+ for (const auto& pair: mActivationBroadcastGuardrailStats) {
+ uint64_t token = proto.start(FIELD_TYPE_MESSAGE |
+ FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL |
+ FIELD_COUNT_REPEATED);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID,
+ (int32_t) pair.first);
+ for (const auto& guardrailHitTime : pair.second) {
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME |
+ FIELD_COUNT_REPEATED,
+ guardrailHitTime);
+ }
+ proto.end(token);
+ }
+
output->clear();
size_t bufferSize = proto.size();
output->resize(bufferSize);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 42d9e96..23d2ace 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -445,6 +445,12 @@
void noteEventQueueOverflow(int64_t oldestEventTimestampNs);
/**
+ * Reports that the activation broadcast guardrail was hit for this uid. Namely, the broadcast
+ * should have been sent, but instead was skipped due to hitting the guardrail.
+ */
+ void noteActivationBroadcastGuardrailHit(const int uid);
+
+ /**
* Reset the historical stats. Including all stats in icebox, and the tracked stats about
* metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
* to collect stats after reset() has been called.
@@ -532,6 +538,10 @@
// Maps metric ID to its stats. The size is capped by the number of metrics.
std::map<int64_t, AtomMetricStats> mAtomMetricStats;
+ // Maps uids to times when the activation changed broadcast not sent due to hitting the
+ // guardrail. The size is capped by the number of configs, and up to 20 times per uid.
+ std::map<int, std::list<int32_t>> mActivationBroadcastGuardrailStats;
+
struct LogLossStats {
LogLossStats(int32_t sec, int32_t count, int32_t error, int32_t tag, int32_t uid,
int32_t pid)
@@ -588,6 +598,8 @@
void noteActiveStatusChanged(const ConfigKey& key, bool activate, int32_t timeSec);
+ void noteActivationBroadcastGuardrailHit(const int uid, int32_t timeSec);
+
void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats);
/**
@@ -607,6 +619,7 @@
FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
+ FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 207a7dd..963205e 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -521,6 +521,10 @@
if (metric->getMetricId() == activeMetric.id()) {
VLOG("Setting active metric: %lld", (long long)metric->getMetricId());
metric->loadActiveMetric(activeMetric, currentTimeNs);
+ if (!mIsActive && metric->isActive()) {
+ StatsdStats::getInstance().noteActiveStatusChanged(mConfigKey,
+ /*activate=*/ true);
+ }
mIsActive |= metric->isActive();
}
}
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 54ca757..b875470 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -470,6 +470,13 @@
}
optional EventQueueOverflow queue_overflow = 18;
+
+ message ActivationBroadcastGuardrail {
+ optional int32 uid = 1;
+ repeated int32 guardrail_met_sec = 2;
+ }
+
+ repeated ActivationBroadcastGuardrail activation_guardrail_stats = 19;
}
message AlertTriggerDetails {
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 1b8a3b5..2a43d9b 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -445,6 +445,47 @@
EXPECT_EQ(StatsdStats::kMaxSystemServerRestarts + 1, report.system_restart_sec(maxCount - 1));
}
+TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit) {
+ StatsdStats stats;
+ int uid1 = 1;
+ int uid2 = 2;
+ stats.noteActivationBroadcastGuardrailHit(uid1, 10);
+ stats.noteActivationBroadcastGuardrailHit(uid1, 20);
+
+ // Test that we only keep 20 timestamps.
+ for (int i = 0; i < 100; i++) {
+ stats.noteActivationBroadcastGuardrailHit(uid2, i);
+ }
+
+ vector<uint8_t> output;
+ stats.dumpStats(&output, false);
+ StatsdStatsReport report;
+ EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
+
+ EXPECT_EQ(2, report.activation_guardrail_stats_size());
+ bool uid1Good = false;
+ bool uid2Good = false;
+ for (const auto& guardrailTimes : report.activation_guardrail_stats()) {
+ if (uid1 == guardrailTimes.uid()) {
+ uid1Good = true;
+ EXPECT_EQ(2, guardrailTimes.guardrail_met_sec_size());
+ EXPECT_EQ(10, guardrailTimes.guardrail_met_sec(0));
+ EXPECT_EQ(20, guardrailTimes.guardrail_met_sec(1));
+ } else if (uid2 == guardrailTimes.uid()) {
+ int maxCount = StatsdStats::kMaxTimestampCount;
+ uid2Good = true;
+ EXPECT_EQ(maxCount, guardrailTimes.guardrail_met_sec_size());
+ for (int i = 0; i < maxCount; i++) {
+ EXPECT_EQ(100 - maxCount + i, guardrailTimes.guardrail_met_sec(i));
+ }
+ } else {
+ FAIL() << "Unexpected uid.";
+ }
+ }
+ EXPECT_TRUE(uid1Good);
+ EXPECT_TRUE(uid2Good);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 3f6880f..650147b 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -177,4 +177,12 @@
* @param displayId the id of the display on which contents are drawn.
*/
void onSingleTaskDisplayDrawn(int displayId);
+
+ /**
+ * Called when a task is reparented to a stack on a different display.
+ *
+ * @param taskId id of the task which was moved to a different display.
+ * @param newDisplayId id of the new display.
+ */
+ void onTaskDisplayChanged(int taskId, int newDisplayId);
}
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 36daf32..b63feb5 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -26,6 +26,7 @@
/**
* Classes interested in observing only a subset of changes using ITaskStackListener can extend
* this class to avoid having to implement all the methods.
+ *
* @hide
*/
public abstract class TaskStackListener extends ITaskStackListener.Stub {
@@ -177,4 +178,8 @@
@Override
public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
}
+
+ @Override
+ public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException {
+ }
}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 4b1796a..bb04a2e 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -53,7 +53,8 @@
* the availability of roles. Instead, they should always query if the role is available using
* {@link #isRoleAvailable(String)} before trying to do anything with it. Some predefined role names
* are available as constants in this class, and a list of possibly available roles can be found in
- * the AndroidX Libraries.
+ * the <a href="{@docRoot}reference/androidx/core/role/package-summary.html">AndroidX Role
+ * library</a>.
* <p>
* There can be multiple applications qualifying for a role, but only a subset of them can become
* role holders. To qualify for a role, an application must meet certain requirements, including
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index e5f3d26..9994f9f 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -67,7 +67,8 @@
void forceUpdateIfaces(
in Network[] defaultNetworks,
in NetworkState[] networkStates,
- in String activeIface);
+ in String activeIface,
+ in VpnInfo[] vpnInfos);
/** Force update of statistics. */
@UnsupportedAppUsage
void forceUpdate();
diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java
index d9bcb3c..489a292 100644
--- a/core/java/android/net/util/SocketUtils.java
+++ b/core/java/android/net/util/SocketUtils.java
@@ -70,7 +70,7 @@
@NonNull
public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex) {
return new PacketSocketAddress(
- (short) protocol /* sll_protocol */,
+ protocol /* sll_protocol */,
ifIndex /* sll_ifindex */,
null /* sll_addr */);
}
@@ -81,7 +81,7 @@
@NonNull
public static SocketAddress makePacketSocketAddress(int ifIndex, @NonNull byte[] hwAddr) {
return new PacketSocketAddress(
- (short) 0 /* sll_protocol */,
+ 0 /* sll_protocol */,
ifIndex /* sll_ifindex */,
hwAddr /* sll_addr */);
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 7f60b9c..1351380 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -246,27 +246,6 @@
**/
/**
- * Return global network statistics summarized at an interface level,
- * without any UID-level granularity.
- */
- NetworkStats getNetworkStatsSummaryDev();
- NetworkStats getNetworkStatsSummaryXt();
-
- /**
- * Return detailed network statistics with UID-level granularity,
- * including interface and tag details.
- */
- NetworkStats getNetworkStatsDetail();
-
- /**
- * Return detailed network statistics for the requested UID and interfaces,
- * including interface and tag details.
- * @param uid UID to obtain statistics for, or {@link NetworkStats#UID_ALL}.
- * @param ifaces Interfaces to obtain statistics for, or {@link NetworkStats#INTERFACES_ALL}.
- */
- NetworkStats getNetworkStatsUidDetail(int uid, in String[] ifaces);
-
- /**
* Return summary of network statistics all tethering interfaces.
*/
NetworkStats getNetworkStatsTethering(int how);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index f99e990..a32b7c6 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -81,7 +81,7 @@
* not be used if the devices has a DeviceConfig profile pushed to it that contains a value for
* this key.
*/
- private static final String USAP_POOL_ENABLED_DEFAULT = "true";
+ private static final String USAP_POOL_ENABLED_DEFAULT = "false";
/**
* The name of the socket used to communicate with the primary zygote.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 37b6f13..4bfd138 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -309,4 +309,10 @@
* Called when the system gesture exclusion has changed.
*/
void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects);
+
+ /**
+ * Request the server to call setInputWindowInfo on a given Surface, and return
+ * an input channel where the client can receive input.
+ */
+ void blessInputSurface(int displayId, in SurfaceControl surface, out InputChannel outInputChannel);
}
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index ecb727c..831e9ee 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -55,6 +55,7 @@
private static native InputChannel[] nativeOpenInputChannelPair(String name);
private native void nativeDispose(boolean finalized);
+ private native void nativeRelease();
private native void nativeTransferTo(InputChannel other);
private native void nativeReadFromParcel(Parcel parcel);
private native void nativeWriteToParcel(Parcel parcel);
@@ -120,6 +121,14 @@
}
/**
+ * Release the Java objects hold over the native InputChannel. If other references
+ * still exist in native-land, then the channel may continue to exist.
+ */
+ public void release() {
+ nativeRelease();
+ }
+
+ /**
* Transfers ownership of the internal state of the input channel to another
* instance and invalidates this instance. This is used to pass an input channel
* as an out parameter in a binder call.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 87dd5b4..60db6a5 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1934,12 +1934,13 @@
public static final boolean isWakeKey(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
+ case KeyEvent.KEYCODE_CAMERA:
case KeyEvent.KEYCODE_MENU:
- case KeyEvent.KEYCODE_WAKEUP:
case KeyEvent.KEYCODE_PAIRING:
case KeyEvent.KEYCODE_STEM_1:
case KeyEvent.KEYCODE_STEM_2:
case KeyEvent.KEYCODE_STEM_3:
+ case KeyEvent.KEYCODE_WAKEUP:
return true;
}
return false;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 01cfae8..cb51545 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -609,10 +609,13 @@
}
private String mTag = TAG;
-
public ViewRootImpl(Context context, Display display) {
+ this(context, display, WindowManagerGlobal.getWindowSession());
+ }
+
+ public ViewRootImpl(Context context, Display display, IWindowSession session) {
mContext = context;
- mWindowSession = WindowManagerGlobal.getWindowSession();
+ mWindowSession = session;
mDisplay = display;
mBasePackageName = context.getBasePackageName();
mThread = Thread.currentThread();
diff --git a/core/java/android/view/WindowlessViewRoot.java b/core/java/android/view/WindowlessViewRoot.java
new file mode 100644
index 0000000..75057a2
--- /dev/null
+++ b/core/java/android/view/WindowlessViewRoot.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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.content.res.Resources;
+import android.content.Context;
+import android.view.SurfaceControl;
+import android.view.View;
+
+/**
+ * Utility class for adding a view hierarchy to a SurfaceControl.
+ *
+ * See WindowlessWmTest for example usage.
+ * @hide
+ */
+public class WindowlessViewRoot {
+ ViewRootImpl mViewRoot;
+ WindowlessWindowManager mWm;
+ public WindowlessViewRoot(Context c, Display d, SurfaceControl rootSurface) {
+ mWm = new WindowlessWindowManager(c.getResources().getConfiguration(), rootSurface);
+ mViewRoot = new ViewRootImpl(c, d, mWm);
+ }
+
+ public void addView(View view, WindowManager.LayoutParams attrs) {
+ mViewRoot.setView(view, attrs, null);
+ }
+}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
new file mode 100644
index 0000000..dfeb4b5
--- /dev/null
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 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.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.MergedConfiguration;
+import android.util.Log;
+import android.view.IWindowSession;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+import java.util.HashMap;
+
+/**
+* A simplistic implementation of IWindowSession. Rather than managing Surfaces
+* as children of the display, it manages Surfaces as children of a given root.
+*
+* By parcelling the root surface, the app can offer another app content for embedding.
+* @hide
+*/
+class WindowlessWindowManager extends IWindowSession.Default {
+ private final static String TAG = "WindowlessWindowManager";
+
+ /**
+ * Used to store SurfaceControl we've built for clients to
+ * reconfigure them if relayout is called.
+ */
+ final HashMap<IBinder, SurfaceControl> mScForWindow = new HashMap<IBinder, SurfaceControl>();
+ final SurfaceSession mSurfaceSession = new SurfaceSession();
+ final SurfaceControl mRootSurface;
+ final Configuration mConfiguration;
+ IWindowSession mRealWm;
+
+ private int mForceHeight = -1;
+ private int mForceWidth = -1;
+
+ WindowlessWindowManager(Configuration c, SurfaceControl rootSurface) {
+ mRootSurface = rootSurface;
+ mConfiguration = new Configuration(c);
+ mRealWm = WindowManagerGlobal.getWindowSession();
+ }
+
+ public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
+ Rect outStableInsets, Rect outOutsets,
+ DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+ InsetsState outInsetsState) {
+ final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
+ .setParent(mRootSurface)
+ .setName(attrs.getTitle().toString());
+ final SurfaceControl sc = b.build();
+ synchronized (this) {
+ mScForWindow.put(window.asBinder(), sc);
+ }
+
+ if ((attrs.inputFeatures &
+ WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
+ try {
+ mRealWm.blessInputSurface(displayId, sc, outInputChannel);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to bless surface: " + e);
+ }
+ }
+
+ return WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
+ }
+
+ @Override
+ public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
+ Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
+ Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
+ DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
+ SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
+ SurfaceControl sc = null;
+ synchronized (this) {
+ sc = mScForWindow.get(window.asBinder());
+ }
+ if (sc == null) {
+ throw new IllegalArgumentException(
+ "Invalid window token (never added or removed already)");
+ }
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.show(sc).setBufferSize(sc, requestedWidth, requestedHeight).apply();
+ outSurfaceControl.copyFrom(sc);
+ outFrame.set(0, 0, requestedWidth, requestedHeight);
+
+ mergedConfiguration.setConfiguration(mConfiguration, mConfiguration);
+
+ return 0;
+ }
+}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 50bb688..c3e08fc 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -95,6 +95,7 @@
private float mTouchDownX;
@UnsupportedAppUsage
private boolean mIsDragging;
+ private float mTouchThumbOffset = 0.0f;
private List<Rect> mUserGestureExclusionRects = Collections.emptyList();
private final List<Rect> mGestureExclusionRects = new ArrayList<>();
@@ -874,6 +875,14 @@
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
+ if (mThumb != null) {
+ final int availableWidth = getWidth() - mPaddingLeft - mPaddingRight;
+ mTouchThumbOffset = (getProgress() - getMin()) / (float) (getMax()
+ - getMin()) - (event.getX() - mPaddingLeft) / availableWidth;
+ if (Math.abs(mTouchThumbOffset * availableWidth) > getThumbOffset()) {
+ mTouchThumbOffset = 0;
+ }
+ }
if (isInScrollingContainer()) {
mTouchDownX = event.getX();
} else {
@@ -956,7 +965,8 @@
} else if (x < mPaddingLeft) {
scale = 1.0f;
} else {
- scale = (availableWidth - x + mPaddingLeft) / (float) availableWidth;
+ scale = (availableWidth - x + mPaddingLeft) / (float) availableWidth
+ + mTouchThumbOffset;
progress = mTouchProgressOffset;
}
} else {
@@ -965,7 +975,7 @@
} else if (x > width - mPaddingRight) {
scale = 1.0f;
} else {
- scale = (x - mPaddingLeft) / (float) availableWidth;
+ scale = (x - mPaddingLeft) / (float) availableWidth + mTouchThumbOffset;
progress = mTouchProgressOffset;
}
}
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index 8b25e2d..3ac58e0 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -16,8 +16,13 @@
package com.android.internal.os;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StrictMode;
import android.os.SystemClock;
+import android.system.suspend.ISuspendControlService;
+import android.system.suspend.WakeLockInfo;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -58,6 +63,7 @@
private final String[] mProcWakelocksName = new String[3];
private final long[] mProcWakelocksData = new long[3];
+ private ISuspendControlService mSuspendControlService = null;
/**
* Reads kernel wakelock stats and updates the staleStats with the new information.
@@ -117,7 +123,52 @@
}
}
}
- return parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
+
+ updateVersion(staleStats);
+
+ parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
+
+ if (mSuspendControlService == null) {
+ try {
+ mSuspendControlService = ISuspendControlService.Stub.asInterface(
+ ServiceManager.getServiceOrThrow("suspend_control"));
+ } catch (ServiceNotFoundException e) {
+ Slog.wtf(TAG, "Required service suspend_control not available", e);
+ }
+ }
+
+ try {
+ WakeLockInfo[] wlStats = mSuspendControlService.getWakeLockStats();
+ getNativeWakelockStats(wlStats, staleStats);
+ } catch (RemoteException e) {
+ Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e);
+ }
+
+ return removeOldStats(staleStats);
+ }
+
+ /**
+ * Reads native wakelock stats from SystemSuspend and updates staleStats with the new
+ * information.
+ * @param staleStats Existing object to update.
+ * @return the updated stats.
+ */
+ @VisibleForTesting
+ public KernelWakelockStats getNativeWakelockStats(WakeLockInfo[] wlStats,
+ final KernelWakelockStats staleStats) {
+ for (WakeLockInfo info : wlStats) {
+ if (!staleStats.containsKey(info.name)) {
+ staleStats.put(info.name, new KernelWakelockStats.Entry((int) info.activeCount,
+ info.totalTime, sKernelWakelockUpdateVersion));
+ } else {
+ KernelWakelockStats.Entry kwlStats = staleStats.get(info.name);
+ kwlStats.mCount = (int) info.activeCount;
+ kwlStats.mTotalTime = info.totalTime;
+ kwlStats.mVersion = sKernelWakelockUpdateVersion;
+ }
+ }
+
+ return staleStats;
}
/**
@@ -138,7 +189,6 @@
startIndex = endIndex = i + 1;
synchronized(this) {
- sKernelWakelockUpdateVersion++;
while (endIndex < len) {
for (endIndex=startIndex;
endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
@@ -199,16 +249,35 @@
startIndex = endIndex + 1;
}
- // Don't report old data.
- Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator();
- while (itr.hasNext()) {
- if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
- itr.remove();
- }
- }
-
- staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion;
return staleStats;
}
}
+
+ /**
+ * Increments sKernelWakelockUpdateVersion and updates the version in staleStats.
+ * @param staleStats Existing object to update.
+ * @return the updated stats.
+ */
+ @VisibleForTesting
+ public KernelWakelockStats updateVersion(KernelWakelockStats staleStats) {
+ sKernelWakelockUpdateVersion++;
+ staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion;
+ return staleStats;
+ }
+
+ /**
+ * Removes old stats from staleStats.
+ * @param staleStats Existing object to update.
+ * @return the updated stats.
+ */
+ @VisibleForTesting
+ public KernelWakelockStats removeOldStats(final KernelWakelockStats staleStats) {
+ Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator();
+ while (itr.hasNext()) {
+ if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
+ itr.remove();
+ }
+ }
+ return staleStats;
+ }
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index afe7954..6f4f337 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -84,6 +84,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
+import android.view.ViewRootImpl;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.Window;
@@ -1150,8 +1151,15 @@
navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
0 /* sideInset */, animate && !disallowAnimate,
mForceWindowDrawsBarBackgrounds);
+ boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground;
mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
&& (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
+ if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
+ ViewRootImpl vri = getViewRootImpl();
+ if (vri != null) {
+ vri.requestInvalidateRootRenderNode();
+ }
+ }
boolean statusBarNeedsRightInset = navBarToRightEdge
&& mNavigationColorViewState.present;
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 82593b5..1fdb1f3 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -458,7 +458,7 @@
}
private String getFuncTypeAsString() {
- if (isRecycled()) throw new IllegalStateException();
+ if (isRecycled()) return "<recycled>";
if (isConstSupplier()) return "supplier";
String name = LambdaType.toString(getFlags(MASK_EXPOSED_AS));
if (name.endsWith("Consumer")) return "consumer";
@@ -466,7 +466,7 @@
if (name.endsWith("Predicate")) return "predicate";
if (name.endsWith("Supplier")) return "supplier";
if (name.endsWith("Runnable")) return "runnable";
- throw new IllegalStateException("Don't know the string representation of " + name);
+ return name;
}
/**
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 73dc656..c74c264 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -23,7 +23,6 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <gui/BufferQueue.h>
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 9819d9a..7e3b083 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -175,6 +175,15 @@
}
}
+static void android_view_InputChannel_nativeRelease(JNIEnv* env, jobject obj, jboolean finalized) {
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ if (nativeInputChannel) {
+ android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
+ delete nativeInputChannel;
+ }
+}
+
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
jobject otherObj) {
if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
@@ -274,6 +283,8 @@
(void*)android_view_InputChannel_nativeOpenInputChannelPair },
{ "nativeDispose", "(Z)V",
(void*)android_view_InputChannel_nativeDispose },
+ { "nativeRelease", "()V",
+ (void*)android_view_InputChannel_nativeRelease },
{ "nativeTransferTo", "(Landroid/view/InputChannel;)V",
(void*)android_view_InputChannel_nativeTransferTo },
{ "nativeReadFromParcel", "(Landroid/os/Parcel;)V",
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 6e41cc9..7c42994 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -34,7 +34,6 @@
#include <gui/Surface.h>
#include <gui/view/Surface.h>
#include <gui/SurfaceControl.h>
-#include <gui/GLConsumer.h>
#include <ui/Rect.h>
#include <ui/Region.h>
diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h
index 47a952f..637b823 100644
--- a/core/jni/include/android_runtime/android_view_Surface.h
+++ b/core/jni/include/android_runtime/android_view_Surface.h
@@ -18,7 +18,6 @@
#define _ANDROID_VIEW_SURFACE_H
#include <android/native_window.h>
-#include <system/graphics.h>
#include <ui/PublicFormat.h>
#include "jni.h"
diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto
index b7bf384..9eaabfb 100644
--- a/core/proto/android/stats/dnsresolver/dns_resolver.proto
+++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto
@@ -67,6 +67,7 @@
// NS_R_BADSIG = 16,
NS_R_BADKEY = 17;
NS_R_BADTIME = 18;
+ NS_R_INTERNAL_ERROR = 254;
NS_R_TIMEOUT = 255;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 62351d0..dea8092 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2526,7 +2526,7 @@
<!-- @SystemApi @TestApi @hide Allows an application to modify config settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_DEVICE_CONFIG"
- android:protectionLevel="signature|configurator"/>
+ android:protectionLevel="signature|verifier|configurator"/>
<!-- @SystemApi @hide Allows an application to read config settings.
<p>Not for use by third-party applications. -->
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 162cd69..6cfbd10 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -321,8 +321,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Einschließlich personenbezogener Daten wie Kreditkartennummern und Passwörter."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Displayvergrößerung festlegen"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Legt die Zoom-Stufe und -Position auf dem Display fest."</string>
- <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Bewegungen möglich"</string>
- <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Tippen, Wischen, Zusammenziehen und andere Bewegungen möglich."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Gesten möglich"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Tippen, Wischen, Zusammenziehen und andere Gesten möglich."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Bewegungen auf dem Fingerabdrucksensor"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"Erfasst Bewegungen auf dem Fingerabdrucksensor des Geräts."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Statusleiste deaktivieren oder ändern"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index d59c75f..a7fbf09 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -34,7 +34,7 @@
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
<string name="mmiError" msgid="5154499457739052907">"Problema de conexión o código incorrecto de MMI."</string>
<string name="mmiFdnError" msgid="5224398216385316471">"La operación está limitada a números de marcación fija."</string>
- <string name="mmiErrorWhileRoaming" msgid="762488890299284230">"No se puede cambiar la configuración de reenvío de llamadas de tu teléfono mientras usas el servicio de roaming."</string>
+ <string name="mmiErrorWhileRoaming" msgid="762488890299284230">"No se puede cambiar la configuración de desvío de llamadas de tu teléfono mientras usas el servicio de roaming."</string>
<string name="serviceEnabled" msgid="8147278346414714315">"Se ha activado el servicio."</string>
<string name="serviceEnabledFor" msgid="6856228140453471041">"Se activó el servicio para:"</string>
<string name="serviceDisabled" msgid="1937553226592516411">"Se ha desactivado el servicio."</string>
@@ -1668,7 +1668,7 @@
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Mantén presionadas ambas teclas de volumen durante tres segundos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Elige un servicio para usar cuando presiones el botón de accesibilidad:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Elige un servicio para usar cuando realices el gesto de accesibilidad (deslizar dos dedos hacia arriba desde la parte inferior de la pantalla):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Elige un servicio para usar cuando realices el gesto de accesibilidad (deslizar tres dedos hacia arriba desde la parte inferior de la pantalla):"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Elige el servicio que se usará cuando realices el gesto de accesibilidad (deslizar tres dedos hacia arriba desde la parte inferior de la pantalla):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Para cambiar de servicio, mantén presionado el botón de accesibilidad."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Para cambiar de servicio, desliza dos dedos hacia arriba y mantén presionado."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="4969448938984394550">"Para cambiar de servicio, desliza tres dedos hacia arriba y mantén presionado."</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 6635da4..d7190b7 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -568,7 +568,7 @@
<string name="face_acquired_too_right" msgid="3667075962661863218">"Mugitu telefonoa ezkerretara."</string>
<string name="face_acquired_too_left" msgid="3148242963894703424">"Mugitu telefonoa eskuinetara."</string>
<string name="face_acquired_poor_gaze" msgid="5606479370806754905">"Begiratu zuzenago gailuari."</string>
- <string name="face_acquired_not_detected" msgid="4885504661626728809">"Ez da agertzen aurpegia. Begiratu telefonoari."</string>
+ <string name="face_acquired_not_detected" msgid="4885504661626728809">"Ez da hautematen aurpegia. Begiratu telefonoari."</string>
<string name="face_acquired_too_much_motion" msgid="3149332171102108851">"Mugimendu gehiegi dago. Eutsi tinko telefonoari."</string>
<string name="face_acquired_recalibrate" msgid="8077949502893707539">"Erregistratu berriro aurpegia."</string>
<string name="face_acquired_too_different" msgid="7663983770123789694">"Ez dugu ezagutzen aurpegi hori. Saiatu berriro."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 37ec0da..7d8da5b 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -568,7 +568,7 @@
<string name="face_acquired_too_right" msgid="3667075962661863218">"Move o teléfono cara á esquerda."</string>
<string name="face_acquired_too_left" msgid="3148242963894703424">"Move o teléfono cara á dereita."</string>
<string name="face_acquired_poor_gaze" msgid="5606479370806754905">"Mira o dispositivo de forma máis directa."</string>
- <string name="face_acquired_not_detected" msgid="4885504661626728809">"Non se pode ver a túa cara. Mira o teléfono."</string>
+ <string name="face_acquired_not_detected" msgid="4885504661626728809">"Non se ve a túa cara. Mira para o teléfono"</string>
<string name="face_acquired_too_much_motion" msgid="3149332171102108851">"Demasiado movemento. Non movas o teléfono."</string>
<string name="face_acquired_recalibrate" msgid="8077949502893707539">"Volve rexistrar a túa cara."</string>
<string name="face_acquired_too_different" msgid="7663983770123789694">"Xa non se pode recoñecer a cara. Téntao de novo."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 2be42bc..2c2b7dd 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -30,7 +30,7 @@
<string name="untitled" msgid="4638956954852782576">"<शीर्षक-रहित>"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(कोई फ़ोन नंबर नहीं)"</string>
<string name="unknownName" msgid="6867811765370350269">"अज्ञात"</string>
- <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"वॉयस मेल"</string>
+ <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"वॉइसमेल"</string>
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
<string name="mmiError" msgid="5154499457739052907">"कनेक्शन समस्या या अमान्य MMI कोड."</string>
<string name="mmiFdnError" msgid="5224398216385316471">"कार्रवाई केवल फ़िक्स्ड डायलिंग नंबर के लिए प्रतिबंधित है."</string>
@@ -97,7 +97,7 @@
<string name="notification_channel_sim" msgid="4052095493875188564">"सिम की स्थिति"</string>
<string name="peerTtyModeFull" msgid="6165351790010341421">"पीयर ने टेलीटाइपराइटर (TTY) मोड फ़ुल का अनुरोध किया"</string>
<string name="peerTtyModeHco" msgid="5728602160669216784">"पीयर ने टेलीटाइपराइटर (TTY) मोड एचसीओ (HCO) का अनुरोध किया"</string>
- <string name="peerTtyModeVco" msgid="1742404978686538049">"पीयर ने टेलीटाइपराइटर (TTY) मोड वीसीअो (VCO) का अनुरोध किया"</string>
+ <string name="peerTtyModeVco" msgid="1742404978686538049">"पीयर ने टेलीटाइपराइटर (TTY) मोड वीसीओ (VCO) का अनुरोध किया"</string>
<string name="peerTtyModeOff" msgid="3280819717850602205">"पीयर ने टेलीटाइपराइटर (TTY) मोड बंद का अनुरोध किया"</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"आवाज़"</string>
<string name="serviceClassData" msgid="872456782077937893">"डेटा"</string>
@@ -336,7 +336,7 @@
<string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"शॉर्टकट अनइंस्टॉल करें"</string>
<string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"ऐप्लिकेशन को उपयोगकर्ता की रोक के बिना होमस्क्रीन शॉर्टकट निकालने की अनुमति देता है."</string>
<string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"किया जाने वाला कॉल (आउटगोइंग) कहीं और भेजें"</string>
- <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"एेप कॉल को किसी और नंबर पर भेजने या कॉल को पूरी तरह रद्द करने के विकल्प के साथ, किए गए कॉल (आउटगोइंग) के नंबर को देख सकता है."</string>
+ <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"ऐप कॉल को किसी और नंबर पर भेजने या कॉल को पूरी तरह रद्द करने के विकल्प के साथ, किए गए कॉल (आउटगोइंग) के नंबर को देख सकता है."</string>
<string name="permlab_answerPhoneCalls" msgid="4077162841226223337">"फ़ोन कॉल का जवाब दें"</string>
<string name="permdesc_answerPhoneCalls" msgid="2901889867993572266">"ऐप्लिकेशन को किसी इनकमिंग फ़ोन कॉल का जवाब देने देती है."</string>
<string name="permlab_receiveSms" msgid="8673471768947895082">"मैसेज (एसएमएस) पाएं"</string>
@@ -400,9 +400,9 @@
<string name="permlab_readCallLog" msgid="3478133184624102739">"कॉल लॉग पढ़ें"</string>
<string name="permdesc_readCallLog" msgid="3204122446463552146">"यह ऐप्लिकेशन आपका कॉल इतिहास पढ़ सकता है."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"कॉल लॉग लिखें"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"एेप को आने वाला कॉल (इनकमिंग) और किया जाने वाला कॉल (आउटगोइंग) डेटा सहित, आपके टैबलेट के कॉल लॉग को बदलने की अनुमति देता है. धोखा देने वाले एेप, इसका इस्तेमाल करके आपके कॉल लॉग को मिटा या बदल सकते हैं."</string>
- <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"एेप को आने वाला कॉल (इनकमिंग) और किया जाने वाला कॉल (आउटगोइंग) डेटा सहित, आपके टीवी के कॉल लॉग को बदलने की अनुमति देता है. धोखा देने वाले एेप, इसका इस्तेमाल करके आपके कॉल लॉग को मिटा या बदल सकते हैं."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"एेप को आने वाला कॉल (इनकमिंग) और किया जाने वाला कॉल (आउटगोइंग) डेटा सहित, आपके फ़ोन के कॉल लॉग को बदलने की अनुमति देता है. धोखा देने वाले एेप, इसका इस्तेमाल करके आपके कॉल लॉग को मिटा या बदल सकते हैं."</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ऐप को आने वाला कॉल (इनकमिंग) और किया जाने वाला कॉल (आउटगोइंग) डेटा सहित, आपके टैबलेट के कॉल लॉग को बदलने की अनुमति देता है. धोखा देने वाले ऐप, इसका इस्तेमाल करके आपके कॉल लॉग को मिटा या बदल सकते हैं."</string>
+ <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"ऐप को आने वाला कॉल (इनकमिंग) और किया जाने वाला कॉल (आउटगोइंग) डेटा सहित, आपके टीवी के कॉल लॉग को बदलने की अनुमति देता है. धोखा देने वाले ऐप, इसका इस्तेमाल करके आपके कॉल लॉग को मिटा या बदल सकते हैं."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ऐप को आने वाला कॉल (इनकमिंग) और किया जाने वाला कॉल (आउटगोइंग) डेटा सहित, आपके फ़ोन के कॉल लॉग को बदलने की अनुमति देता है. धोखा देने वाले ऐप, इसका इस्तेमाल करके आपके कॉल लॉग को मिटा या बदल सकते हैं."</string>
<string name="permlab_bodySensors" msgid="4683341291818520277">"शरीर के लिए बने सेंसर (जैसे हृदय गति मॉनीटर) को एक्सेस करें"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"ऐप को आपकी शारीरिक स्थिति, जैसे आपकी हृदय गति पर नज़र रखने वाले सेंसर के डेटा तक पहुंचने देती है."</string>
<string name="permlab_readCalendar" msgid="6716116972752441641">"कैलेंडर इवेंट और विवरण पढ़ें"</string>
@@ -595,7 +595,7 @@
</string-array>
<string name="face_icon_content_description" msgid="4024817159806482191">"चेहरे का आइकॉन"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"समन्वयन सेटिंग पढ़ें"</string>
- <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ऐप्स को किसी खाते की समन्वयन सेटिंग पढ़ने देता है. उदाहरण के लिए, इससे यह निर्धारित किया जा सकता है कि लोग ऐप्स किसी खाते के साथ समन्वयित है या नहीं."</string>
+ <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ऐप्स को किसी खाते की समन्वयन सेटिंग पढ़ने देता है. उदाहरण के लिए, इससे यह तय किया जा सकता है कि लोग ऐप्स किसी खाते के साथ समन्वयित है या नहीं."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"समन्वयन बंद या चालू टॉगल करें"</string>
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ऐप्स को किसी खाते की समन्वयन सेटिंग संशोधित करने देता है. उदाहरण के लिए, इसका उपयोग लोग ऐप्स का समन्वयन किसी खाते से सक्षम करने में हो सकता है."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"समन्वयन आंकड़े पढ़ें"</string>
@@ -641,7 +641,7 @@
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM प्रमाणपत्र एक्सेस करें"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"ऐप्लिकेशन को DRM प्रमाणपत्रों का प्रावधान और उपयोग करने देती है. सामान्य ऐप्स के लिए कभी भी आवश्यकता नहीं होना चाहिए."</string>
<string name="permlab_handoverStatus" msgid="7820353257219300883">"Android बीम ट्रांसफ़र की स्थिति पाएं"</string>
- <string name="permdesc_handoverStatus" msgid="4788144087245714948">"इस एेप को मौजूदा Android बीम ट्रांसफ़र के बारे में जानकारी पाने की अनुमति दें."</string>
+ <string name="permdesc_handoverStatus" msgid="4788144087245714948">"इस ऐप को मौजूदा Android बीम ट्रांसफ़र के बारे में जानकारी पाने की अनुमति दें."</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM प्रमाणपत्रों को निकाल सकता है"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"ऐप्लिकेशन को DRM प्रमाणपत्रों को निकालने देता है. सामान्य ऐप्स के लिए कभी भी आवश्यकता नहीं होनी चाहिए."</string>
<string name="permlab_bindCarrierMessagingService" msgid="1490229371796969158">"किसी मोबाइल और इंटरनेट सेवा देने वाली कंपनी की संदेश सेवा से जुड़ें"</string>
@@ -1151,7 +1151,7 @@
<string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"चित्र कैप्चर करें"</string>
<string name="alwaysUse" msgid="4583018368000610438">"इस कार्रवाई के लिए डिफ़ॉल्ट के तौर पर इस्तेमाल करें"</string>
<string name="use_a_different_app" msgid="8134926230585710243">"किसी भिन्न ऐप्स का उपयोग करें"</string>
- <string name="clearDefaultHintMsg" msgid="3252584689512077257">"सिस्टम सेटिंग और डाउनलोड किए गए एेप में डिफ़ॉल्ट साफ़ करें."</string>
+ <string name="clearDefaultHintMsg" msgid="3252584689512077257">"सिस्टम सेटिंग और डाउनलोड किए गए ऐप में डिफ़ॉल्ट साफ़ करें."</string>
<string name="chooseActivity" msgid="7486876147751803333">"कोई कार्रवाई चुनें"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"USB डिवाइस के लिए कोई ऐप्स चुनें"</string>
<string name="noApplications" msgid="2991814273936504689">"कोई भी ऐप्स यह कार्यवाही नहीं कर सकता."</string>
@@ -1327,8 +1327,8 @@
<string name="sim_added_message" msgid="6599945301141050216">"मोबाइल नेटवर्क की पहुंच पाने लिए अपना डिवाइस फिर से चालू करें."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"फिर से शुरू करें"</string>
<string name="install_carrier_app_notification_title" msgid="9056007111024059888">"माेबाइल सेवा चालू करें"</string>
- <string name="install_carrier_app_notification_text" msgid="3346681446158696001">"अपना नया सिम चालू करने के लिए मोबाइल और इंटरनेट सेवा देने वाली कंपनी का एेप्लिकेशन डाउनलोड करें"</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"अपना नया सिम चालू करने के लिए <xliff:g id="APP_NAME">%1$s</xliff:g> एेप्लिकेशन डाउनलाेड करें"</string>
+ <string name="install_carrier_app_notification_text" msgid="3346681446158696001">"अपना नया सिम चालू करने के लिए मोबाइल और इंटरनेट सेवा देने वाली कंपनी का ऐप्लिकेशन डाउनलोड करें"</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"अपना नया सिम चालू करने के लिए <xliff:g id="APP_NAME">%1$s</xliff:g> ऐप्लिकेशन डाउनलाेड करें"</string>
<string name="install_carrier_app_notification_button" msgid="3094206295081900849">"ऐप्लिकेशन डाउनलोड करें"</string>
<string name="carrier_app_notification_title" msgid="8921767385872554621">"नई SIM डाली गई"</string>
<string name="carrier_app_notification_text" msgid="1132487343346050225">"इसे सेट करने के लिए टैप करें"</string>
@@ -1551,17 +1551,17 @@
<string name="storage_usb" msgid="3017954059538517278">"USB मेमोरी"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"बदलाव करें"</string>
<string name="data_usage_warning_title" msgid="6499834033204801605">"डेटा खर्च की चेतावनी"</string>
- <string name="data_usage_warning_body" msgid="7340198905103751676">"अाप <xliff:g id="APP">%s</xliff:g> डेटा इस्तेमाल कर चुके हैं"</string>
+ <string name="data_usage_warning_body" msgid="7340198905103751676">"आप <xliff:g id="APP">%s</xliff:g> डेटा इस्तेमाल कर चुके हैं"</string>
<string name="data_usage_mobile_limit_title" msgid="6561099244084267376">"मोबाइल डेटा की सीमा पार हो गई है"</string>
<string name="data_usage_wifi_limit_title" msgid="5803363779034792676">"Wi-Fi डेटा की सीमा पूरी हो गई"</string>
<string name="data_usage_limit_body" msgid="2908179506560812973">"आपकी मौजूदा बिलिंग साइकिल के बाकी दिनों के लिए डेटा का इस्तेमाल राेक दिया गया है"</string>
<string name="data_usage_mobile_limit_snoozed_title" msgid="3171402244827034372">"माेबाइल डेटा की सीमा से ज़्यादा"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="3547771791046344188">"वाई-फ़ाई की सीमा से ज़्यादा डेटा खर्च"</string>
- <string name="data_usage_limit_snoozed_body" msgid="1671222777207603301">"अाप अपनी तय सीमा से <xliff:g id="SIZE">%s</xliff:g> ज़्यादा डेटा इस्तेमाल कर चुके हैं"</string>
+ <string name="data_usage_limit_snoozed_body" msgid="1671222777207603301">"आप अपनी तय सीमा से <xliff:g id="SIZE">%s</xliff:g> ज़्यादा डेटा इस्तेमाल कर चुके हैं"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"पृष्ठभूमि डेटा प्रतिबंधित है"</string>
<string name="data_usage_restricted_body" msgid="469866376337242726">"प्रतिबंध निकालने के लिए टैप करें."</string>
<string name="data_usage_rapid_title" msgid="1809795402975261331">"माेबाइल डेटा का ज़्यादा इस्तेमाल"</string>
- <string name="data_usage_rapid_body" msgid="6897825788682442715">"आपके एेप्लिकेशन ने आम तौर पर इस्तेमाल होने वाले डेटा से ज़्यादा डेटा खर्च कर दिया है"</string>
+ <string name="data_usage_rapid_body" msgid="6897825788682442715">"आपके ऐप्लिकेशन ने आम तौर पर इस्तेमाल होने वाले डेटा से ज़्यादा डेटा खर्च कर दिया है"</string>
<string name="data_usage_rapid_app_body" msgid="5396680996784142544">"<xliff:g id="APP">%s</xliff:g> ने आम तौर पर इस्तेमाल होने वाले डेटा से ज़्यादा डेटा खर्च कर दिया है"</string>
<string name="ssl_certificate" msgid="6510040486049237639">"सुरक्षा प्रमाणपत्र"</string>
<string name="ssl_certificate_is_valid" msgid="6825263250774569373">"यह प्रमाणपत्र मान्य है."</string>
@@ -1984,11 +1984,11 @@
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g>, <xliff:g id="APP_2">%2$s</xliff:g> के हिस्से (स्लाइस) दिखाना चाहता है"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"बदलाव करें"</string>
<string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"कॉल और सूचनाओं के लिए डिवाइस वाइब्रेट हाेगा"</string>
- <string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"कॉल अाैर सूचनाओं के लिए डिवाइस म्यूट रहेगा"</string>
+ <string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"कॉल और सूचनाओं के लिए डिवाइस म्यूट रहेगा"</string>
<string name="notification_channel_system_changes" msgid="5072715579030948646">"सिस्टम में हुए बदलाव"</string>
<string name="notification_channel_do_not_disturb" msgid="6766940333105743037">"परेशान न करें"</string>
<string name="zen_upgrade_notification_visd_title" msgid="3288313883409759733">"नई सुविधा: परेशान न करें सुविधा चालू होने की वजह से सूचनाएं नहीं दिखाई जा रही हैं"</string>
- <string name="zen_upgrade_notification_visd_content" msgid="5533674060311631165">"ज़्यादा जानने अाैर बदलाव करने के लिए टैप करें."</string>
+ <string name="zen_upgrade_notification_visd_content" msgid="5533674060311631165">"ज़्यादा जानने और बदलाव करने के लिए टैप करें."</string>
<string name="zen_upgrade_notification_title" msgid="3799603322910377294">"परेशान न करें की सुविधा बदल गई है"</string>
<string name="zen_upgrade_notification_content" msgid="1794994264692424562">"टैप करके देखें कि किन चीज़ों पर रोक लगाई गई है."</string>
<string name="notification_app_name_system" msgid="4205032194610042794">"सिस्टम"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 18423df..511502f 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -30,7 +30,7 @@
<string name="untitled" msgid="4638956954852782576">"<Tanpa judul>"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Tidak ada nomor telepon)"</string>
<string name="unknownName" msgid="6867811765370350269">"Tidak diketahui"</string>
- <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Kotak Pesan"</string>
+ <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Pesan suara"</string>
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
<string name="mmiError" msgid="5154499457739052907">"Masalah sambungan atau kode MMI tidak valid."</string>
<string name="mmiFdnError" msgid="5224398216385316471">"Operasi dibatasi untuk nomor panggilan tetap saja."</string>
@@ -1506,7 +1506,7 @@
<string name="sync_do_nothing" msgid="3743764740430821845">"Jangan lakukan apa pun untuk saat ini"</string>
<string name="choose_account_label" msgid="5655203089746423927">"Pilih akun"</string>
<string name="add_account_label" msgid="2935267344849993553">"Tambahkan akun"</string>
- <string name="add_account_button_label" msgid="3611982894853435874">"Tambah akun"</string>
+ <string name="add_account_button_label" msgid="3611982894853435874">"Tambahkan akun"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"Tambah"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"Kurangi"</string>
<string name="number_picker_increment_scroll_mode" msgid="5259126567490114216">"<xliff:g id="VALUE">%s</xliff:g> sentuh & tahan."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index a503834..a08b221 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -573,8 +573,8 @@
<string name="face_acquired_recalibrate" msgid="8077949502893707539">"សូមស្កេនបញ្ចូលមុខរបស់អ្នកម្ដងទៀត។"</string>
<string name="face_acquired_too_different" msgid="7663983770123789694">"មិនអាចសម្គាល់មុខបានទៀតទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="face_acquired_too_similar" msgid="1508776858407646460">"ស្រដៀងគ្នាពេក សូមផ្លាស់ប្ដូរកាយវិការរបស់អ្នក។"</string>
- <string name="face_acquired_pan_too_extreme" msgid="4581629343077288178">"ងាកក្បាលរបស់អ្នកបន្តិចទៀត។"</string>
- <string name="face_acquired_tilt_too_extreme" msgid="4019954263012496468">"ងាកក្បាលរបស់អ្នកបន្តិចទៀត។"</string>
+ <string name="face_acquired_pan_too_extreme" msgid="4581629343077288178">"ងាកក្បាលរបស់អ្នកតិចជាងមុនបន្តិច។"</string>
+ <string name="face_acquired_tilt_too_extreme" msgid="4019954263012496468">"ងាកក្បាលរបស់អ្នកតិចជាងមុនបន្តិច។"</string>
<string name="face_acquired_roll_too_extreme" msgid="6312973147689664409">"ងាកក្បាលរបស់អ្នកបន្តិចទៀត។"</string>
<string name="face_acquired_obscured" msgid="5357207702967893283">"យកអ្វីដែលបាំងមុខរបស់អ្នកចេញ។"</string>
<string name="face_acquired_sensor_dirty" msgid="7905138627046865579">"សម្អាតផ្នែកខាងលើនៃអេក្រង់របស់អ្នក រួមទាំងរបារខ្មៅ"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index d3e0de5..cdf4d44 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -594,7 +594,7 @@
<string name="face_error_lockout" msgid="3407426963155388504">"Слишком много попыток. Повторите позже."</string>
<string name="face_error_lockout_permanent" msgid="4723594314443097159">"Слишком много попыток. Функция \"Фейсконтроль\" отключена."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Не удалось распознать лицо. Повторите попытку."</string>
- <string name="face_error_not_enrolled" msgid="4016937174832839540">"Вы не настроили функцию \"Фейсконтроль\"."</string>
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Вы не настроили фейсконтроль."</string>
<string name="face_error_hw_not_present" msgid="8302690289757559738">"Это устройство не поддерживает функцию \"Фейсконтроль\"."</string>
<string name="face_name_template" msgid="7004562145809595384">"Лицо <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 4c6d6e5..b65c5fb 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -88,7 +88,7 @@
<string name="EmergencyCallWarningTitle" msgid="813380189532491336">"ہنگامی کالنگ دستیاب نہیں ہے"</string>
<string name="EmergencyCallWarningSummary" msgid="1899692069750260619">"Wi‑Fi کے ذریعے ہنگامی کالز نہیں کر سکتے"</string>
<string name="notification_channel_network_alert" msgid="4427736684338074967">"الرٹس"</string>
- <string name="notification_channel_call_forward" msgid="2419697808481833249">"کال آگے منتقل کرنا"</string>
+ <string name="notification_channel_call_forward" msgid="2419697808481833249">"کال فارورڈنگ"</string>
<string name="notification_channel_emergency_callback" msgid="6686166232265733921">"ہنگامی کال بیک وضع"</string>
<string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"موبائل ڈیٹا کی صورت حال"</string>
<string name="notification_channel_sms" msgid="3441746047346135073">"SMS پیغامات"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 234c5ce..a62a1fb5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4049,11 +4049,38 @@
</array>
<!-- See DisplayWhiteBalanceController.
- The ambient color temperature (in cct) to which we fall back when the ambient brightness
- drops beneath a certain threshold. -->
+ The ambient color temperature (in cct) to which we interpolate towards using the
+ the look up table generated by config_displayWhiteBalanceLowLightAmbientBrightnesses
+ and config_displayWhiteBalanceLowLightAmbientBiases. -->
<item name="config_displayWhiteBalanceLowLightAmbientColorTemperature" format="float" type="dimen">6500.0</item>
<!-- See DisplayWhiteBalanceController.
+ A float array containing a list of ambient brightnesses, in Lux. This array,
+ together with config_displayWhiteBalanceHighLightAmbientBiases, is used to generate a
+ lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
+ ambient brightness readings to a bias, where the bias is used to linearly interpolate
+ between ambient color temperature and
+ config_displayWhiteBalanceHighLightAmbientColorTemperature.
+ This table is optional. If used, this array must,
+ 1) Contain at least two entries
+ 2) Be the same length as config_displayWhiteBalanceHighLightAmbientBiases. -->
+ <array name ="config_displayWhiteBalanceHighLightAmbientBrightnesses">
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
+ An array containing a list of biases. See
+ config_displayWhiteBalanceHighLightAmbientBrightnesses for additional details.
+ This array must be in the range of [0.0, 1.0]. -->
+ <array name ="config_displayWhiteBalanceHighLightAmbientBiases">
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
+ The ambient color temperature (in cct) to which we interpolate towards using the
+ the look up table generated by config_displayWhiteBalanceHighLightAmbientBrightnesses
+ and config_displayWhiteBalanceHighLightAmbientBiases. -->
+ <item name="config_displayWhiteBalanceHighLightAmbientColorTemperature" format="float" type="dimen">8000.0</item>
+
+ <!-- See DisplayWhiteBalanceController.
A float array containing a list of ambient color temperatures, in Kelvin. This array,
together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a
lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1f3ea20..05c0114 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3755,6 +3755,9 @@
<java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBrightnesses" />
<java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBiases" />
<java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
+ <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBrightnesses" />
+ <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBiases" />
+ <java-symbol type="dimen" name="config_displayWhiteBalanceHighLightAmbientColorTemperature" />
<java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" />
<java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" />
<java-symbol type="drawable" name="ic_action_open" />
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
index 78b6843..008085e 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
@@ -22,6 +22,8 @@
import java.nio.charset.Charset;
+import android.system.suspend.WakeLockInfo;
+
public class KernelWakelockReaderTest extends TestCase {
/**
* Helper class that builds the mock Kernel module file /d/wakeup_sources.
@@ -58,6 +60,39 @@
}
}
+ /**
+ * Helper method to create WakeLockInfo object.
+ * @param totalTime is time in microseconds.
+ * @return the created WakeLockInfo object.
+ */
+ private WakeLockInfo createWakeLockInfo(String name, int activeCount, long totalTime) {
+ WakeLockInfo info = new WakeLockInfo();
+ info.name = name;
+ info.pid = 1;
+ info.activeCount = activeCount;
+ info.isActive = true;
+ info.activeSince = 0;
+ info.lastChange = 0;
+ info.maxTime = 0;
+ info.totalTime = totalTime;
+ return info;
+ }
+
+ /**
+ * Helper method for KernelWakeLockReader::readKernelWakelockStats(...)
+ * @param staleStats existing stats to update.
+ * @param buffer representation of mock kernel module file /d/wakeup_sources.
+ * @param wlStats mock WakeLockInfo list returned from ISuspendControlService.
+ * @return the updated stats.
+ */
+ private KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats,
+ byte[] buffer, WakeLockInfo[] wlStats) {
+ mReader.updateVersion(staleStats);
+ mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats);
+ mReader.getNativeWakelockStats(wlStats, staleStats);
+ return mReader.removeOldStats(staleStats);
+ }
+
private KernelWakelockReader mReader;
@Override
@@ -66,18 +101,22 @@
mReader = new KernelWakelockReader();
}
+// ------------------------- Kernel Wakelock Stats Test ------------------------
@SmallTest
public void testParseEmptyFile() throws Exception {
KernelWakelockStats staleStats = mReader.parseProcWakelocks(new byte[0], 0, true,
new KernelWakelockStats());
+
assertTrue(staleStats.isEmpty());
}
@SmallTest
public void testOnlyHeader() throws Exception {
byte[] buffer = new ProcFileBuilder().getBytes();
+
KernelWakelockStats staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true,
new KernelWakelockStats());
+
assertTrue(staleStats.isEmpty());
}
@@ -86,9 +125,12 @@
byte[] buffer = new ProcFileBuilder()
.addLine("Wakelock", 34, 123) // Milliseconds
.getBytes();
+
KernelWakelockStats staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true,
new KernelWakelockStats());
+
assertEquals(1, staleStats.size());
+
assertTrue(staleStats.containsKey("Wakelock"));
KernelWakelockStats.Entry entry = staleStats.get("Wakelock");
@@ -102,9 +144,12 @@
.addLine("Wakelock", 1, 10)
.addLine("Fakelock", 2, 20)
.getBytes();
+
KernelWakelockStats staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true,
new KernelWakelockStats());
+
assertEquals(2, staleStats.size());
+
assertTrue(staleStats.containsKey("Wakelock"));
assertTrue(staleStats.containsKey("Fakelock"));
}
@@ -115,8 +160,10 @@
.addLine("Wakelock", 1, 10) // Milliseconds
.addLine("Wakelock", 1, 10) // Milliseconds
.getBytes();
+
KernelWakelockStats staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true,
new KernelWakelockStats());
+
assertEquals(1, staleStats.size());
assertTrue(staleStats.containsKey("Wakelock"));
@@ -127,12 +174,14 @@
@SmallTest
public void testWakelocksBecomeStale() throws Exception {
+ KernelWakelockStats staleStats = new KernelWakelockStats();
+
byte[] buffer = new ProcFileBuilder()
.addLine("Fakelock", 3, 30)
.getBytes();
- KernelWakelockStats staleStats = new KernelWakelockStats();
- staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats);
+ readKernelWakelockStats(staleStats, buffer, new WakeLockInfo[0]);
+
assertEquals(1, staleStats.size());
assertTrue(staleStats.containsKey("Fakelock"));
@@ -140,9 +189,228 @@
.addLine("Wakelock", 1, 10)
.getBytes();
- staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats);
+ readKernelWakelockStats(staleStats, buffer, new WakeLockInfo[0]);
+
assertEquals(1, staleStats.size());
assertTrue(staleStats.containsKey("Wakelock"));
assertFalse(staleStats.containsKey("Fakelock"));
}
+
+// -------------------- Native (SystemSuspend) Wakelock Stats Test -------------------
+ @SmallTest
+ public void testEmptyWakeLockInfoList() {
+ KernelWakelockStats staleStats = mReader.getNativeWakelockStats(new WakeLockInfo[0],
+ new KernelWakelockStats());
+
+ assertTrue(staleStats.isEmpty());
+ }
+
+ @SmallTest
+ public void testOneWakeLockInfo() {
+ WakeLockInfo[] wlStats = new WakeLockInfo[1];
+ wlStats[0] = createWakeLockInfo("WakeLock", 20, 10000);
+
+ KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats,
+ new KernelWakelockStats());
+
+ assertEquals(1, staleStats.size());
+
+ assertTrue(staleStats.containsKey("WakeLock"));
+
+ KernelWakelockStats.Entry entry = staleStats.get("WakeLock");
+ assertEquals(20, entry.mCount);
+ assertEquals(10000, entry.mTotalTime);
+ }
+
+ @SmallTest
+ public void testTwoWakeLockInfos() {
+ WakeLockInfo[] wlStats = new WakeLockInfo[2];
+ wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000);
+ wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000);
+
+ KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats,
+ new KernelWakelockStats());
+
+ assertEquals(2, staleStats.size());
+
+ assertTrue(staleStats.containsKey("WakeLock1"));
+ assertTrue(staleStats.containsKey("WakeLock2"));
+
+ KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1");
+ assertEquals(10, entry1.mCount);
+ assertEquals(1000, entry1.mTotalTime);
+
+ KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2");
+ assertEquals(20, entry2.mCount);
+ assertEquals(2000, entry2.mTotalTime);
+ }
+
+ @SmallTest
+ public void testWakeLockInfosBecomeStale() {
+ WakeLockInfo[] wlStats = new WakeLockInfo[1];
+ wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000);
+
+ KernelWakelockStats staleStats = new KernelWakelockStats();
+
+ readKernelWakelockStats(staleStats, new byte[0], wlStats);
+
+ assertEquals(1, staleStats.size());
+
+ assertTrue(staleStats.containsKey("WakeLock1"));
+ KernelWakelockStats.Entry entry = staleStats.get("WakeLock1");
+ assertEquals(10, entry.mCount);
+ assertEquals(1000, entry.mTotalTime);
+
+ wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000);
+
+ readKernelWakelockStats(staleStats, new byte[0], wlStats);
+
+ assertEquals(1, staleStats.size());
+
+ assertFalse(staleStats.containsKey("WakeLock1"));
+ assertTrue(staleStats.containsKey("WakeLock2"));
+ entry = staleStats.get("WakeLock2");
+ assertEquals(20, entry.mCount);
+ assertEquals(2000, entry.mTotalTime);
+ }
+
+// -------------------- Aggregate Wakelock Stats Tests --------------------
+ @SmallTest
+ public void testAggregateStatsEmpty() throws Exception {
+ KernelWakelockStats staleStats = new KernelWakelockStats();
+
+ byte[] buffer = new byte[0];
+ WakeLockInfo[] wlStats = new WakeLockInfo[0];
+
+ readKernelWakelockStats(staleStats, buffer, wlStats);
+
+ assertTrue(staleStats.isEmpty());
+ }
+
+ @SmallTest
+ public void testAggregateStatsNoNativeWakelocks() throws Exception {
+ KernelWakelockStats staleStats = new KernelWakelockStats();
+
+ byte[] buffer = new ProcFileBuilder()
+ .addLine("Wakelock", 34, 123) // Milliseconds
+ .getBytes();
+ WakeLockInfo[] wlStats = new WakeLockInfo[0];
+
+ readKernelWakelockStats(staleStats, buffer, wlStats);
+
+ assertEquals(1, staleStats.size());
+
+ assertTrue(staleStats.containsKey("Wakelock"));
+
+ KernelWakelockStats.Entry entry = staleStats.get("Wakelock");
+ assertEquals(34, entry.mCount);
+ assertEquals(1000 * 123, entry.mTotalTime); // Microseconds
+ }
+
+ @SmallTest
+ public void testAggregateStatsNoKernelWakelocks() throws Exception {
+ KernelWakelockStats staleStats = new KernelWakelockStats();
+
+ byte[] buffer = new byte[0];
+ WakeLockInfo[] wlStats = new WakeLockInfo[1];
+ wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000);
+
+ readKernelWakelockStats(staleStats, buffer, wlStats);
+
+ assertEquals(1, staleStats.size());
+
+ assertTrue(staleStats.containsKey("WakeLock"));
+
+ KernelWakelockStats.Entry entry = staleStats.get("WakeLock");
+ assertEquals(10, entry.mCount);
+ assertEquals(1000, entry.mTotalTime);
+ }
+
+ @SmallTest
+ public void testAggregateStatsBothKernelAndNativeWakelocks() throws Exception {
+ KernelWakelockStats staleStats = new KernelWakelockStats();
+
+ byte[] buffer = new ProcFileBuilder()
+ .addLine("WakeLock1", 34, 123) // Milliseconds
+ .getBytes();
+ WakeLockInfo[] wlStats = new WakeLockInfo[1];
+ wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000);
+
+ readKernelWakelockStats(staleStats, buffer, wlStats);
+
+ assertEquals(2, staleStats.size());
+
+ assertTrue(staleStats.containsKey("WakeLock1"));
+ KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1");
+ assertEquals(34, entry1.mCount);
+ assertEquals(123 * 1000, entry1.mTotalTime); // Microseconds
+
+ assertTrue(staleStats.containsKey("WakeLock2"));
+ KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2");
+ assertEquals(10, entry2.mCount);
+ assertEquals(1000, entry2.mTotalTime);
+ }
+
+ @SmallTest
+ public void testAggregateStatsUpdate() throws Exception {
+ KernelWakelockStats staleStats = new KernelWakelockStats();
+
+ byte[] buffer = new ProcFileBuilder()
+ .addLine("WakeLock1", 34, 123) // Milliseconds
+ .addLine("WakeLock2", 46, 345) // Milliseconds
+ .getBytes();
+ WakeLockInfo[] wlStats = new WakeLockInfo[2];
+ wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000);
+ wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000);
+
+ readKernelWakelockStats(staleStats, buffer, wlStats);
+
+ assertEquals(4, staleStats.size());
+
+ assertTrue(staleStats.containsKey("WakeLock1"));
+ assertTrue(staleStats.containsKey("WakeLock2"));
+ assertTrue(staleStats.containsKey("WakeLock3"));
+ assertTrue(staleStats.containsKey("WakeLock4"));
+
+ KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1");
+ assertEquals(34, entry1.mCount);
+ assertEquals(123 * 1000, entry1.mTotalTime); // Microseconds
+
+ KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2");
+ assertEquals(46, entry2.mCount);
+ assertEquals(345 * 1000, entry2.mTotalTime); // Microseconds
+
+ KernelWakelockStats.Entry entry3 = staleStats.get("WakeLock3");
+ assertEquals(10, entry3.mCount);
+ assertEquals(1000, entry3.mTotalTime);
+
+ KernelWakelockStats.Entry entry4 = staleStats.get("WakeLock4");
+ assertEquals(20, entry4.mCount);
+ assertEquals(2000, entry4.mTotalTime);
+
+ buffer = new ProcFileBuilder()
+ .addLine("WakeLock1", 45, 789) // Milliseconds
+ .addLine("WakeLock1", 56, 123) // Milliseconds
+ .getBytes();
+ wlStats = new WakeLockInfo[1];
+ wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000);
+
+ readKernelWakelockStats(staleStats, buffer, wlStats);
+
+ assertEquals(2, staleStats.size());
+
+ assertTrue(staleStats.containsKey("WakeLock1"));
+ assertTrue(staleStats.containsKey("WakeLock4"));
+
+ assertFalse(staleStats.containsKey("WakeLock2"));
+ assertFalse(staleStats.containsKey("WakeLock3"));
+
+ entry1 = staleStats.get("WakeLock1");
+ assertEquals(45 + 56, entry1.mCount);
+ assertEquals((789 + 123) * 1000, entry1.mTotalTime); // Microseconds
+
+ entry2 = staleStats.get("WakeLock4");
+ assertEquals(40, entry2.mCount);
+ assertEquals(4000, entry4.mTotalTime);
+ }
}
diff --git a/data/sounds/AudioTv.mk b/data/sounds/AudioTv.mk
index d0006b7..2a31e4c 100644
--- a/data/sounds/AudioTv.mk
+++ b/data/sounds/AudioTv.mk
@@ -15,6 +15,7 @@
LOCAL_PATH := frameworks/base/data/sounds
PRODUCT_COPY_FILES += \
+ $(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c9203d9..550c27d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -19,7 +19,6 @@
#include "../Properties.h"
#include "AnimationContext.h"
-#include "EglManager.h"
#include "Frame.h"
#include "LayerUpdateQueue.h"
#include "Properties.h"
@@ -33,8 +32,6 @@
#include "utils/TimeUtils.h"
#include "utils/TraceUtils.h"
-#include <cutils/properties.h>
-#include <private/hwui/DrawGlInfo.h>
#include <strings.h>
#include <fcntl.h>
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 20247dc..3dd1d44 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -22,11 +22,8 @@
#include "Readback.h"
#include "Rect.h"
#include "WebViewFunctorManager.h"
-#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/VectorDrawableAtlas.h"
-#include "renderstate/RenderState.h"
#include "renderthread/CanvasContext.h"
-#include "renderthread/EglManager.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
#include "utils/Macros.h"
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index a667660..4f64da7 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -83,42 +83,45 @@
}
windowInfo->actualSize = windowSize;
- if (windowInfo->transform & HAL_TRANSFORM_ROT_90) {
+ if (windowInfo->transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
windowInfo->actualSize.set(windowSize.height(), windowSize.width());
}
windowInfo->preTransform = GetPreTransformMatrix(windowInfo->size, windowInfo->transform);
}
-static bool ResetNativeWindow(ANativeWindow* window) {
- // -- Reset the native window --
- // The native window might have been used previously, and had its properties
- // changed from defaults. That will affect the answer we get for queries
- // like MIN_UNDEQUEUED_BUFFERS. Reset to a known/default state before we
- // attempt such queries.
+static bool ConnectAndSetWindowDefaults(ANativeWindow* window) {
+ ATRACE_CALL();
int err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
if (err != 0) {
- ALOGW("native_window_api_connect failed: %s (%d)", strerror(-err), err);
+ ALOGE("native_window_api_connect failed: %s (%d)", strerror(-err), err);
return false;
}
// this will match what we do on GL so pick that here.
err = window->setSwapInterval(window, 1);
if (err != 0) {
- ALOGW("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err);
+ ALOGE("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err);
return false;
}
err = native_window_set_shared_buffer_mode(window, false);
if (err != 0) {
- ALOGW("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err);
+ ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err);
return false;
}
err = native_window_set_auto_refresh(window, false);
if (err != 0) {
- ALOGW("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err);
+ ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err);
+ return false;
+ }
+
+ err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE);
+ if (err != 0) {
+ ALOGE("native_window_set_scaling_mode(NATIVE_WINDOW_SCALING_MODE_FREEZE) failed: %s (%d)",
+ strerror(-err), err);
return false;
}
@@ -133,69 +136,92 @@
const SkISize minSize = SkISize::Make(1, 1);
const SkISize maxSize = SkISize::Make(4096, 4096);
- /*
- * Populate Window Info struct
- */
- WindowInfo windowInfo;
+ // Connect and set native window to default configurations.
+ if (!ConnectAndSetWindowDefaults(window)) {
+ return nullptr;
+ }
- int err;
+ // Initialize WindowInfo struct.
+ WindowInfo windowInfo;
+ if (!InitializeWindowInfoStruct(window, colorMode, colorType, colorSpace, vkManager,
+ extraBuffers, minSize, maxSize, &windowInfo)) {
+ return nullptr;
+ }
+
+ // Now we attempt to modify the window.
+ if (!UpdateWindow(window, windowInfo)) {
+ return nullptr;
+ }
+
+ return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext);
+}
+
+bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode,
+ SkColorType colorType,
+ sk_sp<SkColorSpace> colorSpace,
+ const VulkanManager& vkManager,
+ uint32_t extraBuffers, const SkISize& minSize,
+ const SkISize& maxSize, WindowInfo* outWindowInfo) {
+ ATRACE_CALL();
+
int width, height;
- err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
+ int err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
if (err != 0 || width < 0) {
ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, width);
- return nullptr;
+ return false;
}
err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
if (err != 0 || height < 0) {
ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, height);
- return nullptr;
+ return false;
}
- windowInfo.size = SkISize::Make(width, height);
+ outWindowInfo->size = SkISize::Make(width, height);
int query_value;
err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &query_value);
if (err != 0 || query_value < 0) {
ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
- return nullptr;
+ return false;
}
- windowInfo.transform = query_value;
+ outWindowInfo->transform = query_value;
- ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize);
+ ComputeWindowSizeAndTransform(outWindowInfo, minSize, maxSize);
err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
if (err != 0 || query_value < 0) {
ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
- return nullptr;
+ return false;
}
- windowInfo.bufferCount = static_cast<uint32_t>(query_value) + sTargetBufferCount + extraBuffers;
+ outWindowInfo->bufferCount =
+ static_cast<uint32_t>(query_value) + sTargetBufferCount + extraBuffers;
err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
if (err != 0 || query_value < 0) {
ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
- return nullptr;
+ return false;
}
- if (windowInfo.bufferCount > static_cast<uint32_t>(query_value)) {
+ if (outWindowInfo->bufferCount > static_cast<uint32_t>(query_value)) {
// Application must settle for fewer images than desired:
- windowInfo.bufferCount = static_cast<uint32_t>(query_value);
+ outWindowInfo->bufferCount = static_cast<uint32_t>(query_value);
}
- windowInfo.dataspace = HAL_DATASPACE_V0_SRGB;
+ outWindowInfo->dataspace = HAL_DATASPACE_V0_SRGB;
if (colorMode == ColorMode::WideColorGamut) {
skcms_Matrix3x3 surfaceGamut;
LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&surfaceGamut),
"Could not get gamut matrix from color space");
if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) {
- windowInfo.dataspace = HAL_DATASPACE_V0_SCRGB;
+ outWindowInfo->dataspace = HAL_DATASPACE_V0_SCRGB;
} else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) {
- windowInfo.dataspace = HAL_DATASPACE_DISPLAY_P3;
+ outWindowInfo->dataspace = HAL_DATASPACE_DISPLAY_P3;
} else {
LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
}
}
- windowInfo.pixelFormat = ColorTypeToPixelFormat(colorType);
+ outWindowInfo->pixelFormat = ColorTypeToPixelFormat(colorType);
VkFormat vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM;
- if (windowInfo.pixelFormat == PIXEL_FORMAT_RGBA_FP16) {
+ if (outWindowInfo->pixelFormat == PIXEL_FORMAT_RGBA_FP16) {
vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
}
@@ -232,31 +258,23 @@
vkManager.mPhysicalDevice, &imageFormatInfo, &imgFormProps);
if (VK_SUCCESS != res) {
ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2");
- return nullptr;
+ return false;
}
uint64_t consumerUsage;
- native_window_get_consumer_usage(window, &consumerUsage);
- windowInfo.windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage;
-
- /*
- * Now we attempt to modify the window!
- */
- if (!UpdateWindow(window, windowInfo)) {
- return nullptr;
+ err = native_window_get_consumer_usage(window, &consumerUsage);
+ if (err != 0) {
+ ALOGE("native_window_get_consumer_usage failed: %s (%d)", strerror(-err), err);
+ return false;
}
+ outWindowInfo->windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage;
- return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext);
+ return true;
}
bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) {
ATRACE_CALL();
- if (!ResetNativeWindow(window)) {
- return false;
- }
-
- // -- Configure the native window --
int err = native_window_set_buffers_format(window, windowInfo.pixelFormat);
if (err != 0) {
ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)",
@@ -295,16 +313,6 @@
return false;
}
- // Vulkan defaults to NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, but this is different than
- // HWUI's expectation
- err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE);
- if (err != 0) {
- ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) "
- "failed: %s (%d)",
- strerror(-err), err);
- return false;
- }
-
err = native_window_set_buffer_count(window, windowInfo.bufferCount);
if (err != 0) {
ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)",
@@ -319,7 +327,7 @@
return false;
}
- return err == 0;
+ return true;
}
VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index b7af596..54007e7 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -103,6 +103,11 @@
VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, SkISize minWindowSize,
SkISize maxWindowSize, GrContext* grContext);
+ static bool InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode,
+ SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
+ const VulkanManager& vkManager, uint32_t extraBuffers,
+ const SkISize& minSize, const SkISize& maxSize,
+ WindowInfo* outWindowInfo);
static bool UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo);
static void ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize,
const SkISize& maxSize);
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index 06bd692..e09eeb8 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -277,28 +277,36 @@
@Override
public boolean equals(Object o) {
if (this == o) {
- return true;
+ return true;
}
if (!(o instanceof TvTrackInfo)) {
- return false;
+ return false;
}
TvTrackInfo obj = (TvTrackInfo) o;
- return TextUtils.equals(mId, obj.mId)
- && mType == obj.mType
- && TextUtils.equals(mLanguage, obj.mLanguage)
- && TextUtils.equals(mDescription, obj.mDescription)
- && mEncrypted == obj.mEncrypted
- && Objects.equals(mExtra, obj.mExtra)
- && (mType == TYPE_AUDIO
- ? mAudioChannelCount == obj.mAudioChannelCount
- && mAudioSampleRate == obj.mAudioSampleRate
- : (mType == TYPE_VIDEO
- ? mVideoWidth == obj.mVideoWidth
- && mVideoHeight == obj.mVideoHeight
- && mVideoFrameRate == obj.mVideoFrameRate
- && mVideoPixelAspectRatio == obj.mVideoPixelAspectRatio : true));
+
+ if (!TextUtils.equals(mId, obj.mId) || mType != obj.mType
+ || !TextUtils.equals(mLanguage, obj.mLanguage)
+ || !TextUtils.equals(mDescription, obj.mDescription)
+ || !Objects.equals(mExtra, obj.mExtra)) {
+ return false;
+ }
+
+ switch (mType) {
+ case TYPE_AUDIO:
+ return mAudioChannelCount == obj.mAudioChannelCount
+ && mAudioSampleRate == obj.mAudioSampleRate;
+
+ case TYPE_VIDEO:
+ return mVideoWidth == obj.mVideoWidth
+ && mVideoHeight == obj.mVideoHeight
+ && mVideoFrameRate == obj.mVideoFrameRate
+ && mVideoPixelAspectRatio == obj.mVideoPixelAspectRatio
+ && mVideoActiveFormatDescription == obj.mVideoActiveFormatDescription;
+ }
+
+ return true;
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index f298b59..358e2cd 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -346,7 +346,7 @@
CarSystemUIFactory factory = SystemUIFactory.getInstance();
mCarFacetButtonController = factory.getCarDependencyComponent()
- .getCarFacetButtonController();
+ .getCarFacetButtonController();
mNotificationPanelBackground = getDefaultWallpaper();
mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
@@ -637,13 +637,14 @@
}
Rect rect = mNotificationView.getClipBounds();
- if (rect != null) {
+ if (rect != null && rect.bottom != to) {
float from = rect.bottom;
animate(from, to, velocity, isClosing);
return;
}
- // We will only be here if the shade is being opened programmatically.
+ // We will only be here if the shade is being opened programmatically or via button when
+ // height of the layout was not calculated.
ViewTreeObserver notificationTreeObserver = mNotificationView.getViewTreeObserver();
notificationTreeObserver.addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@@ -924,6 +925,16 @@
Log.e(TAG, "Getting StackInfo from activity manager failed", e);
}
}
+
+ @Override
+ public void onTaskDisplayChanged(int taskId, int newDisplayId) {
+ try {
+ mCarFacetButtonController.taskChanged(
+ ActivityTaskManager.getService().getAllStackInfos());
+ } catch (Exception e) {
+ Log.e(TAG, "Getting StackInfo from activity manager failed", e);
+ }
+ }
}
private void onDrivingStateChanged(CarDrivingStateEvent notUsed) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 402ce90..df30c24 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -35,12 +35,12 @@
import com.android.settingslib.R;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* BluetoothEventManager receives broadcasts and callbacks from the Bluetooth
@@ -56,7 +56,7 @@
private final Map<String, Handler> mHandlerMap;
private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
private final BroadcastReceiver mProfileBroadcastReceiver = new BluetoothBroadcastReceiver();
- private final Collection<BluetoothCallback> mCallbacks = new ArrayList<>();
+ private final Collection<BluetoothCallback> mCallbacks = new CopyOnWriteArrayList<>();
private final android.os.Handler mReceiverHandler;
private final UserHandle mUserHandle;
private final Context mContext;
@@ -93,8 +93,10 @@
new ConnectionStateChangedHandler());
// Discovery broadcasts
- addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
- addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
+ addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED,
+ new ScanningStateChangedHandler(true));
+ addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED,
+ new ScanningStateChangedHandler(false));
addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());
addHandler(BluetoothDevice.ACTION_ALIAS_CHANGED, new NameChangedHandler());
@@ -128,16 +130,12 @@
/** Register to start receiving callbacks for Bluetooth events. */
public void registerCallback(BluetoothCallback callback) {
- synchronized (mCallbacks) {
- mCallbacks.add(callback);
- }
+ mCallbacks.add(callback);
}
/** Unregister to stop receiving callbacks for Bluetooth events. */
public void unregisterCallback(BluetoothCallback callback) {
- synchronized (mCallbacks) {
- mCallbacks.remove(callback);
- }
+ mCallbacks.remove(callback);
}
@VisibleForTesting
@@ -189,63 +187,48 @@
}
void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onDeviceAdded(cachedDevice);
- }
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onDeviceAdded(cachedDevice);
}
}
void dispatchDeviceRemoved(CachedBluetoothDevice cachedDevice) {
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onDeviceDeleted(cachedDevice);
- }
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onDeviceDeleted(cachedDevice);
}
}
void dispatchProfileConnectionStateChanged(CachedBluetoothDevice device, int state,
int bluetoothProfile) {
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
- }
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
}
}
private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onConnectionStateChanged(cachedDevice, state);
- }
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onConnectionStateChanged(cachedDevice, state);
}
}
private void dispatchAudioModeChanged() {
mDeviceManager.dispatchAudioModeChanged();
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onAudioModeChanged();
- }
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onAudioModeChanged();
}
}
private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
mDeviceManager.onActiveDeviceChanged(activeDevice, bluetoothProfile);
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
- }
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
}
}
- private void dispatchAclStateChanged(CachedBluetoothDevice activeDevice,
- int state) {
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onAclConnectionStateChanged(activeDevice, state);
- }
+ private void dispatchAclStateChanged(CachedBluetoothDevice activeDevice, int state) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onAclConnectionStateChanged(activeDevice, state);
}
}
@@ -270,17 +253,14 @@
}
private class AdapterStateChangedHandler implements Handler {
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR);
// update local profiles and get paired devices
mLocalAdapter.setBluetoothStateInt(state);
// send callback to update UI and possibly start scanning
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onBluetoothStateChanged(state);
- }
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onBluetoothStateChanged(state);
}
// Inform CachedDeviceManager that the adapter state has changed
mDeviceManager.onBluetoothStateChanged(state);
@@ -293,12 +273,10 @@
ScanningStateChangedHandler(boolean started) {
mStarted = started;
}
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onScanningStateChanged(mStarted);
- }
+
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onScanningStateChanged(mStarted);
}
mDeviceManager.onScanningStateChanged(mStarted);
}
@@ -317,7 +295,7 @@
Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
+ cachedDevice);
} else if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
- &&!cachedDevice.getDevice().isConnected()) {
+ && !cachedDevice.getDevice().isConnected()) {
// Dispatch device add callback to show bonded but
// not connected devices in discovery mode
dispatchDeviceAdded(cachedDevice);
@@ -350,8 +328,7 @@
}
private class BondStateChangedHandler implements Handler {
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
if (device == null) {
Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
return;
@@ -365,10 +342,8 @@
cachedDevice = mDeviceManager.addDevice(device);
}
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onDeviceBondStateChanged(cachedDevice, bondState);
- }
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onDeviceBondStateChanged(cachedDevice, bondState);
}
cachedDevice.onBondingStateChanged(bondState);
@@ -388,12 +363,12 @@
* Called when we have reached the unbonded state.
*
* @param reason one of the error reasons from
- * BluetoothDevice.UNBOND_REASON_*
+ * BluetoothDevice.UNBOND_REASON_*
*/
private void showUnbondMessage(Context context, String name, int reason) {
int errorMsg;
- switch(reason) {
+ switch (reason) {
case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
errorMsg = R.string.bluetooth_pairing_pin_error_message;
break;
@@ -410,7 +385,8 @@
errorMsg = R.string.bluetooth_pairing_error_message;
break;
default:
- Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
+ Log.w(TAG,
+ "showUnbondMessage: Not displaying any message for reason: " + reason);
return;
}
BluetoothUtils.showError(context, name, errorMsg);
@@ -418,8 +394,7 @@
}
private class ClassChangedHandler implements Handler {
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
if (cachedDevice != null) {
cachedDevice.refresh();
@@ -428,8 +403,7 @@
}
private class UuidChangedHandler implements Handler {
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
if (cachedDevice != null) {
cachedDevice.onUuidChanged();
@@ -438,8 +412,7 @@
}
private class BatteryLevelChangedHandler implements Handler {
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
if (cachedDevice != null) {
cachedDevice.refresh();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9a95288..84f5a04 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -38,6 +38,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* CachedBluetoothDevice represents a remote Bluetooth device. It contains
@@ -74,7 +75,7 @@
boolean mJustDiscovered;
- private final Collection<Callback> mCallbacks = new ArrayList<>();
+ private final Collection<Callback> mCallbacks = new CopyOnWriteArrayList<>();
/**
* Last time a bt profile auto-connect was attempted.
@@ -678,22 +679,16 @@
}
public void registerCallback(Callback callback) {
- synchronized (mCallbacks) {
- mCallbacks.add(callback);
- }
+ mCallbacks.add(callback);
}
public void unregisterCallback(Callback callback) {
- synchronized (mCallbacks) {
- mCallbacks.remove(callback);
- }
+ mCallbacks.remove(callback);
}
void dispatchAttributesChanged() {
- synchronized (mCallbacks) {
- for (Callback callback : mCallbacks) {
- callback.onDeviceAttributesChanged();
- }
+ for (Callback callback : mCallbacks) {
+ callback.onDeviceAttributesChanged();
}
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4b4912c..17274f4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -644,7 +644,8 @@
<provider
android:name="com.android.keyguard.clock.ClockOptionsProvider"
android:authorities="com.android.keyguard.clock"
- android:exported="true"
+ android:enabled="false"
+ android:exported="false"
android:grantUriPermissions="true">
</provider>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e97055f0..2e2666e 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -94,6 +94,10 @@
<item type="id" name="top_roundess_animator_start_tag"/>
<item type="id" name="top_roundess_animator_end_tag"/>
+ <item type="id" name="keyguard_hun_animator_tag"/>
+ <item type="id" name="keyguard_hun_animator_start_tag"/>
+ <item type="id" name="keyguard_hun_animator_end_tag"/>
+
<!-- Accessibility actions for the notification menu -->
<item type="id" name="action_snooze_undo"/>
<item type="id" name="action_snooze_shorter"/>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index bd2b19c..c215d0f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -87,6 +87,14 @@
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { }
/**
+ * Called when a task is reparented to a stack on a different display.
+ *
+ * @param taskId id of the task which was moved to a different display.
+ * @param newDisplayId id of the new display.
+ */
+ public void onTaskDisplayChanged(int taskId, int newDisplayId) { }
+
+ /**
* Checks that the current user matches the process. Since
* {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
* {@link TaskStackChangeListener} should make this call to verify that we don't act on events
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index c89f2ab..d570a58 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -112,7 +112,7 @@
@Override
public void onPinnedActivityRestartAttempt(boolean clearedTask)
- throws RemoteException{
+ throws RemoteException {
mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0)
.sendToTarget();
@@ -154,7 +154,7 @@
public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
int requestedDisplayId) throws RemoteException {
mHandler.obtainMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
- requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
+ requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
}
@Override
@@ -208,6 +208,11 @@
0 /* unused */).sendToTarget();
}
+ @Override
+ public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException {
+ mHandler.obtainMessage(H.ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
+ }
+
private final class H extends Handler {
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -228,6 +233,7 @@
private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
private static final int ON_SINGLE_TASK_DISPLAY_DRAWN = 19;
+ private static final int ON_TASK_DISPLAY_CHANGED = 20;
public H(Looper looper) {
@@ -313,7 +319,7 @@
final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i)
- .onActivityLaunchOnSecondaryDisplayRerouted(info);
+ .onActivityLaunchOnSecondaryDisplayRerouted(info);
}
break;
}
@@ -370,6 +376,12 @@
}
break;
}
+ case ON_TASK_DISPLAY_CHANGED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onTaskDisplayChanged(msg.arg1, msg.arg2);
+ }
+ break;
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index cc7ac8d..2483192 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -50,6 +50,7 @@
public class KeyguardClockSwitch extends RelativeLayout {
private static final String TAG = "KeyguardClockSwitch";
+ private static final boolean CUSTOM_CLOCKS_ENABLED = false;
/**
* Animation fraction when text is transitioned to/from bold.
@@ -198,7 +199,9 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mClockManager.addOnClockChangedListener(mClockChangedListener);
+ if (CUSTOM_CLOCKS_ENABLED) {
+ mClockManager.addOnClockChangedListener(mClockChangedListener);
+ }
mStatusBarStateController.addCallback(mStateListener);
mSysuiColorExtractor.addOnColorsChangedListener(mColorsListener);
updateColors();
@@ -207,7 +210,9 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mClockManager.removeOnClockChangedListener(mClockChangedListener);
+ if (CUSTOM_CLOCKS_ENABLED) {
+ mClockManager.removeOnClockChangedListener(mClockChangedListener);
+ }
mStatusBarStateController.removeCallback(mStateListener);
mSysuiColorExtractor.removeOnColorsChangedListener(mColorsListener);
setClockPlugin(null);
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index e9eda4f..9a0e9fc 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -19,6 +19,7 @@
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Rect;
+import android.os.HandlerThread;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.util.Size;
@@ -45,12 +46,27 @@
// We delayed destroy render context that subsequent render requests have chance to cancel it.
// This is to avoid destroying then recreating render context in a very short time.
private static final int DELAY_FINISH_RENDERING = 1000;
+ private HandlerThread mWorker;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWorker = new HandlerThread(TAG);
+ mWorker.start();
+ }
@Override
public Engine onCreateEngine() {
return new GLEngine(this);
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mWorker.quitSafely();
+ mWorker = null;
+ }
+
class GLEngine extends Engine implements GLWallpaperRenderer.SurfaceProxy, StateListener {
// Surface is rejected if size below a threshold on some devices (ie. 8px on elfin)
// set min to 64 px (CTS covers this), please refer to ag/4867989 for detail.
@@ -98,13 +114,14 @@
@Override
public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
float yOffsetStep, int xPixelOffset, int yPixelOffset) {
- mRenderer.updateOffsets(xOffset, yOffset);
+ mWorker.getThreadHandler().post(() -> mRenderer.updateOffsets(xOffset, yOffset));
}
@Override
public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
- mRenderer.updateAmbientMode(inAmbientMode,
- (mNeedTransition || animationDuration != 0) ? animationDuration : 0);
+ long duration = mNeedTransition || animationDuration != 0 ? animationDuration : 0;
+ mWorker.getThreadHandler().post(
+ () -> mRenderer.updateAmbientMode(inAmbientMode, duration));
}
@Override
@@ -113,53 +130,61 @@
mController.removeCallback(this /* StateListener */);
}
mController = null;
- mRenderer.finish();
- mRenderer = null;
- mEglHelper.finish();
- mEglHelper = null;
- getSurfaceHolder().getSurface().hwuiDestroy();
+
+ mWorker.getThreadHandler().post(() -> {
+ mRenderer.finish();
+ mRenderer = null;
+ mEglHelper.finish();
+ mEglHelper = null;
+ getSurfaceHolder().getSurface().hwuiDestroy();
+ });
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
- mEglHelper.init(holder);
- mRenderer.onSurfaceCreated();
+ mWorker.getThreadHandler().post(() -> {
+ mEglHelper.init(holder);
+ mRenderer.onSurfaceCreated();
+ });
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- mRenderer.onSurfaceChanged(width, height);
- mNeedRedraw = true;
+ mWorker.getThreadHandler().post(() -> {
+ mRenderer.onSurfaceChanged(width, height);
+ mNeedRedraw = true;
+ });
}
@Override
public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
- if (mNeedRedraw) {
- preRender();
- requestRender();
- postRender();
- mNeedRedraw = false;
- }
- }
-
- @Override
- public SurfaceHolder getHolder() {
- return getSurfaceHolder();
+ mWorker.getThreadHandler().post(() -> {
+ if (mNeedRedraw) {
+ preRender();
+ requestRender();
+ postRender();
+ mNeedRedraw = false;
+ }
+ });
}
@Override
public void onStatePostChange() {
// When back to home, we try to release EGL, which is preserved in lock screen or aod.
if (mController.getState() == StatusBarState.SHADE) {
- scheduleFinishRendering();
+ mWorker.getThreadHandler().post(this::scheduleFinishRendering);
}
}
@Override
public void preRender() {
+ mWorker.getThreadHandler().post(this::preRenderInternal);
+ }
+
+ private void preRenderInternal() {
boolean contextRecreated = false;
Rect frame = getSurfaceHolder().getSurfaceFrame();
- getMainThreadHandler().removeCallbacks(mFinishRenderingTask);
+ cancelFinishRenderingTask();
// Check if we need to recreate egl context.
if (!mEglHelper.hasEglContext()) {
@@ -187,6 +212,10 @@
@Override
public void requestRender() {
+ mWorker.getThreadHandler().post(this::requestRenderInternal);
+ }
+
+ private void requestRenderInternal() {
Rect frame = getSurfaceHolder().getSurfaceFrame();
boolean readyToRender = mEglHelper.hasEglContext() && mEglHelper.hasEglSurface()
&& frame.width() > 0 && frame.height() > 0;
@@ -205,12 +234,16 @@
@Override
public void postRender() {
- scheduleFinishRendering();
+ mWorker.getThreadHandler().post(this::scheduleFinishRendering);
+ }
+
+ private void cancelFinishRenderingTask() {
+ mWorker.getThreadHandler().removeCallbacks(mFinishRenderingTask);
}
private void scheduleFinishRendering() {
- getMainThreadHandler().removeCallbacks(mFinishRenderingTask);
- getMainThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING);
+ cancelFinishRenderingTask();
+ mWorker.getThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING);
}
private void finishRendering() {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/PerimeterPathGuide.java b/packages/SystemUI/src/com/android/systemui/assist/ui/PerimeterPathGuide.java
index 8eea368..65a9fcc 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/PerimeterPathGuide.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/PerimeterPathGuide.java
@@ -287,9 +287,12 @@
float accum = 0;
for (int i = 0; i < mRegions.length; i++) {
mRegions[i].normalizedLength = mRegions[i].absoluteLength / perimeterLength;
- accum += mRegions[i].normalizedLength;
- mRegions[i].endCoordinate = accum;
+ accum += mRegions[i].absoluteLength;
+ mRegions[i].endCoordinate = accum / perimeterLength;
}
+ // Ensure that the last coordinate is 1. Setting it explicitly to avoid floating point
+ // error.
+ mRegions[mRegions.length - 1].endCoordinate = 1f;
}
private CircularCornerPathRenderer.Corner getRotatedCorner(
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 1521889..3cc8ec9a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -96,7 +96,7 @@
mInternalFalsingManager = new FalsingManagerImpl(context);
} else {
mInternalFalsingManager = new BrightLineFalsingManager(
- new FalsingDataProvider(context),
+ new FalsingDataProvider(context.getResources().getDisplayMetrics()),
Dependency.get(AsyncSensorManager.class)
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
index 4975e63..8b11ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -16,7 +16,6 @@
package com.android.systemui.classifier.brightline;
-import android.content.Context;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
@@ -51,8 +50,7 @@
private MotionEvent mFirstRecentMotionEvent;
private MotionEvent mLastMotionEvent;
- public FalsingDataProvider(Context context) {
- DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+ public FalsingDataProvider(DisplayMetrics displayMetrics) {
mXdpi = displayMetrics.xdpi;
mYdpi = displayMetrics.ydpi;
mWidthPixels = displayMetrics.widthPixels;
@@ -145,12 +143,20 @@
boolean isHorizontal() {
recalculateData();
+ if (mRecentMotionEvents.isEmpty()) {
+ return false;
+ }
+
return Math.abs(mFirstRecentMotionEvent.getX() - mLastMotionEvent.getX()) > Math
.abs(mFirstRecentMotionEvent.getY() - mLastMotionEvent.getY());
}
boolean isRight() {
recalculateData();
+ if (mRecentMotionEvents.isEmpty()) {
+ return false;
+ }
+
return mLastMotionEvent.getX() > mFirstRecentMotionEvent.getX();
}
@@ -160,6 +166,10 @@
boolean isUp() {
recalculateData();
+ if (mRecentMotionEvents.isEmpty()) {
+ return false;
+ }
+
return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY();
}
@@ -168,8 +178,13 @@
return;
}
- mFirstRecentMotionEvent = mRecentMotionEvents.get(0);
- mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
+ if (mRecentMotionEvents.isEmpty()) {
+ mFirstRecentMotionEvent = null;
+ mLastMotionEvent = null;
+ } else {
+ mFirstRecentMotionEvent = mRecentMotionEvents.get(0);
+ mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
+ }
calculateAngleInternal();
@@ -245,5 +260,7 @@
}
mRecentMotionEvents.clear();
+
+ mDirty = true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index 1744c4e..d7411260 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -60,6 +60,9 @@
*/
public class EglHelper {
private static final String TAG = EglHelper.class.getSimpleName();
+ // Below two constants make drawing at low priority, so other things can preempt our drawing.
+ private static final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100;
+ private static final int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
private EGLDisplay mEglDisplay;
private EGLConfig mEglConfig;
@@ -181,7 +184,8 @@
* @return true if EglContext is ready.
*/
public boolean createEglContext() {
- int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+ int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_NONE};
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
if (mEglContext == EGL_NO_CONTEXT) {
Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
index b615a5f..60ea1cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
@@ -17,7 +17,6 @@
package com.android.systemui.glwallpaper;
import android.util.Size;
-import android.view.SurfaceHolder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -83,12 +82,6 @@
interface SurfaceProxy {
/**
- * Get surface holder.
- * @return surface holder.
- */
- SurfaceHolder getHolder();
-
- /**
* Ask proxy to start rendering frame to surface.
*/
void requestRender();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index 3f1ff33..4597b16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -43,7 +43,6 @@
private static final String HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE =
"heads_up_status_bar_view_super_parcelable";
private static final String FIRST_LAYOUT = "first_layout";
- private static final String PUBLIC_MODE = "public_mode";
private static final String VISIBILITY = "visibility";
private static final String ALPHA = "alpha";
private int mAbsoluteStartPadding;
@@ -54,7 +53,6 @@
private Rect mLayoutedIconRect = new Rect();
private int[] mTmpPosition = new int[2];
private boolean mFirstLayout = true;
- private boolean mPublicMode;
private int mMaxWidth;
private View mRootView;
private int mSysWinInset;
@@ -121,7 +119,6 @@
bundle.putParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE,
super.onSaveInstanceState());
bundle.putBoolean(FIRST_LAYOUT, mFirstLayout);
- bundle.putBoolean(PUBLIC_MODE, mPublicMode);
bundle.putInt(VISIBILITY, getVisibility());
bundle.putFloat(ALPHA, getAlpha());
@@ -139,7 +136,6 @@
Parcelable superState = bundle.getParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE);
super.onRestoreInstanceState(superState);
mFirstLayout = bundle.getBoolean(FIRST_LAYOUT, true);
- mPublicMode = bundle.getBoolean(PUBLIC_MODE, false);
if (bundle.containsKey(VISIBILITY)) {
setVisibility(bundle.getInt(VISIBILITY));
}
@@ -166,11 +162,13 @@
if (entry != null) {
mShowingEntry = entry;
CharSequence text = entry.headsUpStatusBarText;
- if (mPublicMode) {
+ if (entry.isSensitive()) {
text = entry.headsUpStatusBarTextPublic;
}
mTextView.setText(text);
- } else {
+ mShowingEntry.setOnSensitiveChangedListener(() -> setEntry(entry));
+ } else if (mShowingEntry != null){
+ mShowingEntry.setOnSensitiveChangedListener(null);
mShowingEntry = null;
}
}
@@ -273,10 +271,6 @@
mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
}
- public void setPublicMode(boolean publicMode) {
- mPublicMode = publicMode;
- }
-
public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
mOnDrawingRectChangedListener = onDrawingRectChangedListener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 0b3c6a5..787cc97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -161,7 +161,7 @@
boolean deviceSensitive = devicePublic
&& !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
currentUserId);
- ent.getRow().setSensitive(sensitive, deviceSensitive);
+ ent.setSensitive(sensitive, deviceSensitive);
ent.getRow().setNeedsRedaction(needsRedaction);
if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
NotificationEntry summary = mGroupManager.getGroupSummary(ent.notification);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index bdc4d2a..986486a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -36,9 +36,12 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.policy.HeadsUpManager
import javax.inject.Inject
import javax.inject.Singleton
@@ -51,7 +54,9 @@
class PulseExpansionHandler @Inject
constructor(context: Context,
private val wakeUpCoordinator: NotificationWakeUpCoordinator,
- private val bypassController: KeyguardBypassController) : Gefingerpoken {
+ private val bypassController: KeyguardBypassController,
+ private val headsUpManager: HeadsUpManagerPhone,
+ private val roundnessManager: NotificationRoundnessManager) : Gefingerpoken {
companion object {
private val RUBBERBAND_FACTOR_STATIC = 0.25f
private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
@@ -67,9 +72,20 @@
val changed = field != value
field = value
bypassController.isPulseExpanding = value
- if (changed && !value && !leavingLockscreen) {
- bypassController.maybePerformPendingUnlock()
- pulseExpandAbortListener?.run()
+ if (changed) {
+ if (value) {
+ val topEntry = headsUpManager.topEntry
+ topEntry?.let {
+ roundnessManager.setTrackingHeadsUp(it.row)
+ }
+ } else {
+ roundnessManager.setTrackingHeadsUp(null)
+ if (!leavingLockscreen) {
+ bypassController.maybePerformPendingUnlock()
+ pulseExpandAbortListener?.run()
+ }
+ }
+ headsUpManager.unpinAll(true /* userUnPinned */)
}
}
var leavingLockscreen: Boolean = false
@@ -231,7 +247,7 @@
}
private fun captureStartingChild(x: Float, y: Float) {
- if (mStartingChild == null) {
+ if (mStartingChild == null && !bypassController.bypassEnabled) {
mStartingChild = findView(x, y)
if (mStartingChild != null) {
setUserLocked(mStartingChild!!, true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 95af9fd..6a3816c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -38,7 +38,7 @@
class NotificationWakeUpCoordinator @Inject constructor(
private val mContext: Context,
private val mHeadsUpManagerPhone: HeadsUpManagerPhone,
- private val mStatusBarStateController: StatusBarStateController,
+ private val statusBarStateController: StatusBarStateController,
private val bypassController: KeyguardBypassController)
: OnHeadsUpChangedListener, StatusBarStateController.StateListener {
@@ -69,6 +69,8 @@
private val mDozeParameters: DozeParameters
private var pulseExpanding: Boolean = false
private val wakeUpListeners = arrayListOf<WakeUpListener>()
+ private var state: Int = StatusBarState.KEYGUARD
+
var fullyAwake: Boolean = false
var willWakeUp = false
@@ -111,14 +113,14 @@
if (bypassController.bypassEnabled) {
// We also allow pulsing on the lock screen!
canShow = canShow || (mWakingUp || willWakeUp || fullyAwake)
- && mStatusBarStateController.state == StatusBarState.KEYGUARD
+ && statusBarStateController.state == StatusBarState.KEYGUARD
}
return canShow
}
init {
mHeadsUpManagerPhone.addListener(this)
- mStatusBarStateController.addCallback(this)
+ statusBarStateController.addCallback(this)
mDozeParameters = DozeParameters.getInstance(mContext)
}
@@ -217,13 +219,21 @@
override fun onStateChanged(newState: Int) {
updateDozeAmountIfBypass();
+ if (bypassController.bypassEnabled &&
+ newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED
+ && (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
+ // We're leaving shade locked. Let's animate the notifications away
+ setNotificationsVisible(visible = true, increaseSpeed = false, animate = false)
+ setNotificationsVisible(visible = false, increaseSpeed = false, animate = true)
+ }
+ this.state = newState
}
private fun updateDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
var amount = 1.0f;
- if (mStatusBarStateController.state == StatusBarState.SHADE
- || mStatusBarStateController.state == StatusBarState.SHADE_LOCKED) {
+ if (statusBarStateController.state == StatusBarState.SHADE
+ || statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
amount = 0.0f;
}
setDozeAmount(amount, amount)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index aacb22d..fe56552 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -115,7 +115,20 @@
view.setTag(animationEndTag, newEndValue);
}
- public static <T extends View> boolean isAnimating(T view, AnimatableProperty property) {
- return view.getTag(property.getAnimatorTag()) != null;
+ public static <T extends View> void applyImmediately(T view, AnimatableProperty property,
+ float newValue) {
+ cancelAnimation(view, property);
+ property.getProperty().set(view, newValue);
+ }
+
+ public static void cancelAnimation(View view, AnimatableProperty property) {
+ ValueAnimator animator = (ValueAnimator) view.getTag(property.getAnimatorTag());
+ if (animator != null) {
+ animator.cancel();
+ }
+ }
+
+ public static boolean isAnimating(View view, AnimatableProperty property) {
+ return view.getTag(property.getAnimatorTag()) != null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 9184dec..9db715d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -173,6 +173,8 @@
* the lock screen/status bar and in the top section in the shade.
*/
private boolean mHighPriority;
+ private boolean mSensitive = true;
+ private Runnable mOnSensitiveChangedListener;
public NotificationEntry(StatusBarNotification n) {
this(n, null);
@@ -859,6 +861,30 @@
return Objects.equals(n.category, category);
}
+ /**
+ * Set this notification to be sensitive.
+ *
+ * @param sensitive true if the content of this notification is sensitive right now
+ * @param deviceSensitive true if the device in general is sensitive right now
+ */
+ public void setSensitive(boolean sensitive, boolean deviceSensitive) {
+ getRow().setSensitive(sensitive, deviceSensitive);
+ if (sensitive != mSensitive) {
+ mSensitive = sensitive;
+ if (mOnSensitiveChangedListener != null) {
+ mOnSensitiveChangedListener.run();
+ }
+ }
+ }
+
+ public boolean isSensitive() {
+ return mSensitive;
+ }
+
+ public void setOnSensitiveChangedListener(Runnable listener) {
+ mOnSensitiveChangedListener = listener;
+ }
+
/** Information about a suggestion that is being edited. */
public static class EditedSuggestionInfo {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 6d9d5ec..dca152f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -1040,6 +1040,10 @@
return false;
}
+ public int getHeadsUpHeightWithoutHeader() {
+ return getHeight();
+ }
+
public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 0ca8814..ae534d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2637,7 +2637,7 @@
private int getHeadsUpHeight() {
- return getShowingLayout().getHeadsUpHeight();
+ return getShowingLayout().getHeadsUpHeight(false /* forceNoHeader */);
}
public boolean areGutsExposed() {
@@ -2775,6 +2775,17 @@
}
@Override
+ public int getHeadsUpHeightWithoutHeader() {
+ if (!canShowHeadsUp() || !mIsHeadsUp) {
+ return getCollapsedHeight();
+ }
+ if (mIsSummaryWithChildren && !shouldShowPublic()) {
+ return mChildrenContainer.getCollapsedHeightWithoutHeader();
+ }
+ return getShowingLayout().getHeadsUpHeight(true /* forceNoHeader */);
+ }
+
+ @Override
public void setClipTopAmount(int clipTopAmount) {
super.setClipTopAmount(clipTopAmount);
for (NotificationContentView l : mLayouts) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index d6b7cf3..90f6324 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -713,11 +713,15 @@
}
private int getViewHeight(int visibleType) {
+ return getViewHeight(visibleType, false /* forceNoHeader */);
+ }
+
+ private int getViewHeight(int visibleType, boolean forceNoHeader) {
View view = getViewForVisibleType(visibleType);
int height = view.getHeight();
NotificationViewWrapper viewWrapper = getWrapperForView(view);
if (viewWrapper != null) {
- height += viewWrapper.getHeaderTranslation();
+ height += viewWrapper.getHeaderTranslation(forceNoHeader);
}
return height;
}
@@ -1748,14 +1752,15 @@
return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
}
- public int getHeadsUpHeight() {
+ public int getHeadsUpHeight(boolean forceNoHeader) {
int viewType = VISIBLE_TYPE_HEADSUP;
if (mHeadsUpChild == null) {
viewType = VISIBLE_TYPE_CONTRACTED;
}
// The headsUp remote input quickly switches to the expanded one, so lets also include that
// one
- return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
+ return getViewHeight(viewType, forceNoHeader)
+ + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
+ getExtraRemoteInputHeight(mExpandedRemoteInput);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 7ebdb93..97d8443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -51,7 +51,7 @@
*/
public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
- private final int mTranslationForHeader;
+ private final int mFullHeaderTranslation;
protected ImageView mPicture;
private ProgressBar mProgressBar;
private TextView mTitle;
@@ -135,7 +135,7 @@
}
}, TRANSFORMING_VIEW_TEXT);
- mTranslationForHeader = ctx.getResources().getDimensionPixelSize(
+ mFullHeaderTranslation = ctx.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin)
- ctx.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin_top);
@@ -340,20 +340,20 @@
// We also need to compensate for any header translation, since we're always at the end.
mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()
- - getHeaderTranslation());
+ - getHeaderTranslation(false /* forceNoHeader */));
}
}
@Override
- public int getHeaderTranslation() {
- return (int) mHeaderTranslation;
+ public int getHeaderTranslation(boolean forceNoHeader) {
+ return forceNoHeader ? mFullHeaderTranslation : (int) mHeaderTranslation;
}
@Override
public void setHeaderVisibleAmount(float headerVisibleAmount) {
super.setHeaderVisibleAmount(headerVisibleAmount);
mNotificationHeader.setAlpha(headerVisibleAmount);
- mHeaderTranslation = (1.0f - headerVisibleAmount) * mTranslationForHeader;
+ mHeaderTranslation = (1.0f - headerVisibleAmount) * mFullHeaderTranslation;
mView.setTranslationY(mHeaderTranslation);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 3808702..47906a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -224,7 +224,7 @@
return null;
}
- public int getHeaderTranslation() {
+ public int getHeaderTranslation(boolean forceNoHeader) {
return 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 1242431..45f7b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1067,6 +1067,11 @@
false /* likeHighPriority */);
}
+ public int getCollapsedHeightWithoutHeader() {
+ return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */),
+ false /* likeHighPriority */, 0);
+ }
+
/**
* Get the minimum Height for this group.
*
@@ -1074,10 +1079,22 @@
* @param likeHighPriority if the height should be calculated as if it were not low priority
*/
private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority) {
+ return getMinHeight(maxAllowedVisibleChildren, likeHighPriority, mCurrentHeaderTranslation);
+ }
+
+ /**
+ * Get the minimum Height for this group.
+ *
+ * @param maxAllowedVisibleChildren the number of children that should be visible
+ * @param likeHighPriority if the height should be calculated as if it were not low priority
+ * @param headerTranslation the translation amount of the header
+ */
+ private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority,
+ int headerTranslation) {
if (!likeHighPriority && showingAsLowPriority()) {
return mNotificationHeaderLowPriority.getHeight();
}
- int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
+ int minExpandHeight = mNotificationHeaderMargin + headerTranslation;
int visibleChildren = 0;
boolean firstChild = true;
int childCount = mChildren.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index d8ccd3a..4221846 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -18,10 +18,14 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
+
+import android.util.MathUtils;
+
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.HashSet;
@@ -33,12 +37,13 @@
* A class that manages the roundness for notification views
*/
@Singleton
-class NotificationRoundnessManager implements OnHeadsUpChangedListener {
+public class NotificationRoundnessManager implements OnHeadsUpChangedListener {
private final ActivatableNotificationView[] mFirstInSectionViews;
private final ActivatableNotificationView[] mLastInSectionViews;
private final ActivatableNotificationView[] mTmpFirstInSectionViews;
private final ActivatableNotificationView[] mTmpLastInSectionViews;
+ private final KeyguardBypassController mBypassController;
private boolean mExpanded;
private HashSet<ExpandableView> mAnimatedChildren;
private Runnable mRoundingChangedCallback;
@@ -46,11 +51,12 @@
private float mAppearFraction;
@Inject
- NotificationRoundnessManager() {
+ NotificationRoundnessManager(KeyguardBypassController keyguardBypassController) {
mFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
mLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
mTmpFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
mTmpLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
+ mBypassController = keyguardBypassController;
}
@Override
@@ -124,18 +130,18 @@
if ((view.isPinned() || view.isHeadsUpAnimatingAway()) && !mExpanded) {
return 1.0f;
}
- if (view.showingPulsing()) {
- return 1.0f;
- }
if (isFirstInSection(view, true /* include first section */) && top) {
return 1.0f;
}
if (isLastInSection(view, true /* include last section */) && !top) {
return 1.0f;
}
- if (view == mTrackedHeadsUp && mAppearFraction <= 0.0f) {
+ if (view == mTrackedHeadsUp) {
// If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be
// rounded.
+ return MathUtils.saturate(1.0f - mAppearFraction);
+ }
+ if (view.showingPulsing() && !mBypassController.getBypassEnabled()) {
return 1.0f;
}
return 0.0f;
@@ -232,6 +238,10 @@
}
public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+ ExpandableNotificationRow previous = mTrackedHeadsUp;
mTrackedHeadsUp = row;
+ if (previous != null) {
+ updateView(previous, true /* animate */);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 44e5aef..e2cb8d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -466,12 +466,10 @@
private int mCornerRadius;
private int mSidePaddings;
private final Rect mBackgroundAnimationRect = new Rect();
- private int mAntiBurnInOffsetX;
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private NotificationIconAreaController mIconAreaController;
- private float mHorizontalPanelTranslation;
private final NotificationLockscreenUserManager mLockscreenUserManager =
Dependency.get(NotificationLockscreenUserManager.class);
private final Rect mTmpRect = new Rect();
@@ -500,6 +498,8 @@
mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
private final NotificationSectionsManager mSectionsManager;
private boolean mAnimateBottomOnLayout;
+ private float mLastSentAppear;
+ private float mLastSentExpandedHeight;
@Inject
public NotificationStackScrollLayout(
@@ -561,13 +561,13 @@
res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
- addOnExpandedHeightListener(mRoundnessManager::setExpanded);
+ addOnExpandedHeightChangedListener(mRoundnessManager::setExpanded);
setOutlineProvider(mOutlineProvider);
// Blocking helper manager wants to know the expanded state, update as well.
NotificationBlockingHelperManager blockingHelperManager =
Dependency.get(NotificationBlockingHelperManager.class);
- addOnExpandedHeightListener((height, unused) -> {
+ addOnExpandedHeightChangedListener((height, unused) -> {
blockingHelperManager.setNotificationShadeExpanded(height);
});
@@ -634,7 +634,11 @@
public float getWakeUpHeight() {
ActivatableNotificationView firstChild = getFirstChildWithBackground();
if (firstChild != null) {
- return firstChild.getCollapsedHeight();
+ if (mKeyguardBypassController.getBypassEnabled()) {
+ return firstChild.getHeadsUpHeightWithoutHeader();
+ } else {
+ return firstChild.getCollapsedHeight();
+ }
}
return 0f;
}
@@ -1306,7 +1310,7 @@
stackHeight = (int) height;
}
} else {
- appearFraction = getAppearFraction(height);
+ appearFraction = calculateAppearFraction(height);
if (appearFraction >= 0) {
translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
appearFraction);
@@ -1329,9 +1333,26 @@
requestChildrenUpdate();
}
setStackTranslation(translationY);
- for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
- BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
- listener.accept(mExpandedHeight, appearFraction);
+ notifyAppearChangedListeners();
+ }
+
+ private void notifyAppearChangedListeners() {
+ float appear;
+ float expandAmount;
+ if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) {
+ appear = calculateAppearFractionBypass();
+ expandAmount = getPulseHeight();
+ } else {
+ appear = MathUtils.saturate(calculateAppearFraction(mExpandedHeight));
+ expandAmount = mExpandedHeight;
+ }
+ if (appear != mLastSentAppear || expandAmount != mLastSentExpandedHeight) {
+ mLastSentAppear = appear;
+ mLastSentExpandedHeight = expandAmount;
+ for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
+ BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
+ listener.accept(expandAmount, appear);
+ }
}
}
@@ -1453,7 +1474,7 @@
* @return the fraction of the appear animation that has been performed
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- public float getAppearFraction(float height) {
+ public float calculateAppearFraction(float height) {
float appearEndPosition = getAppearEndPosition();
float appearStartPosition = getAppearStartPosition();
return (height - appearStartPosition)
@@ -4707,17 +4728,6 @@
notifyHeightChangeListener(mShelf);
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void updatePanelTranslation() {
- setTranslationX(mHorizontalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedHideAmount);
- }
-
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setHorizontalPanelTranslation(float verticalPanelTranslation) {
- mHorizontalPanelTranslation = verticalPanelTranslation;
- updatePanelTranslation();
- }
-
/**
* Sets the current hide amount.
*
@@ -4737,7 +4747,7 @@
boolean nowFullyHidden = mAmbientState.isFullyHidden();
boolean nowHiddenAtAll = mAmbientState.isHiddenAtAll();
if (nowFullyHidden != wasFullyHidden) {
- setVisibility(mAmbientState.isFullyHidden() ? View.INVISIBLE : View.VISIBLE);
+ updateVisibility();
}
if (!wasHiddenAtAll && nowHiddenAtAll) {
resetExposedMenuView(true /* animate */, true /* animate */);
@@ -4747,10 +4757,14 @@
}
updateAlgorithmHeightAndPadding();
updateBackgroundDimming();
- updatePanelTranslation();
requestChildrenUpdate();
}
+ private void updateVisibility() {
+ boolean shouldShow = !mAmbientState.isFullyHidden() || !onKeyguard();
+ setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void notifyHideAnimationStart(boolean hide) {
// We only swap the scaling factor if we're fully hidden or fully awake to avoid
@@ -5241,7 +5255,7 @@
boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
if (mHeadsUpAppearanceController != null) {
- mHeadsUpAppearanceController.setPublicMode(publicMode);
+ mHeadsUpAppearanceController.onStateChanged();
}
SysuiStatusBarStateController state = (SysuiStatusBarStateController)
@@ -5259,6 +5273,7 @@
onUpdateRowStates();
mEntryManager.updateNotifications();
+ updateVisibility();
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5297,12 +5312,6 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
- mAntiBurnInOffsetX = antiBurnInOffsetX;
- updatePanelTranslation();
- }
-
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+ " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
@@ -5360,13 +5369,13 @@
}
/**
- * Add a listener whenever the expanded height changes. The first value passed as an argument
- * is the expanded height and the second one is the appearFraction.
+ * Add a listener whenever the expanded height changes. The first value passed as an
+ * argument is the expanded height and the second one is the appearFraction.
*
* @param listener the listener to notify.
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+ public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
mExpandedHeightListeners.add(listener);
}
@@ -5374,7 +5383,7 @@
* Stop a listener from listening to the expandedHeight.
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+ public void removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
mExpandedHeightListeners.remove(listener);
}
@@ -5595,6 +5604,9 @@
*/
public float setPulseHeight(float height) {
mAmbientState.setPulseHeight(height);
+ if (mKeyguardBypassController.getBypassEnabled()) {
+ notifyAppearChangedListeners();
+ }
requestChildrenUpdate();
return Math.max(0, height - mAmbientState.getInnerHeight(true /* ignorePulseHeight */));
}
@@ -5654,6 +5666,16 @@
mAmbientState.setOnPulseHeightChangedListener(listener);
}
+ public float calculateAppearFractionBypass() {
+ float pulseHeight = getPulseHeight();
+ float wakeUpHeight = getWakeUpHeight();
+ float dragDownAmount = pulseHeight - wakeUpHeight;
+
+ // The total distance required to fully reveal the header
+ float totalDistance = getIntrinsicPadding();
+ return MathUtils.smoothStep(0, totalDistance, dragDownAmount);
+ }
+
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 58f457e..d655b2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -35,6 +35,7 @@
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -201,20 +202,21 @@
}
protected int adjustDisableFlags(int state) {
+ boolean headsUpVisible = mStatusBarComponent.headsUpShouldBeVisible();
+ if (headsUpVisible) {
+ state |= DISABLE_CLOCK;
+ }
+
if (!mKeyguardMonitor.isLaunchTransitionFadingAway()
&& !mKeyguardMonitor.isKeyguardFadingAway()
- && shouldHideNotificationIcons()) {
+ && shouldHideNotificationIcons()
+ && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+ && headsUpVisible)) {
state |= DISABLE_NOTIFICATION_ICONS;
state |= DISABLE_SYSTEM_INFO;
state |= DISABLE_CLOCK;
}
- // In landscape, the heads up show but shouldHideNotificationIcons() return false
- // because the visual icon is in notification icon area rather than heads up's space.
- // whether the notification icon show or not, clock should hide when heads up show.
- if (mStatusBarComponent.isHeadsUpShouldBeVisible()) {
- state |= DISABLE_CLOCK;
- }
if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
if (mNetworkController.hasEmergencyCryptKeeperText()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index a243855..60e381a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -149,6 +149,14 @@
mHandler.removeCallbacks(mPulseOut);
}
+ /**
+ * When pulsing, cancel any timeouts that would take you out of the pulsing state.
+ */
+ public void cancelPendingPulseTimeout() {
+ mHandler.removeCallbacks(mPulseOut);
+ mHandler.removeCallbacks(mPulseOutExtended);
+ }
+
private void cancelPulsing() {
if (mPulseCallback != null) {
if (DEBUG) Log.d(TAG, "Cancel pulsing");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 66903fa..46dd5e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.SysUiServiceProvider.getComponent;
+
import android.graphics.Point;
import android.graphics.Rect;
import android.view.DisplayCutout;
@@ -27,11 +29,16 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.function.BiConsumer;
@@ -56,13 +63,16 @@
private final Consumer<ExpandableNotificationRow>
mSetTrackingHeadsUp = this::setTrackingHeadsUp;
private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
- private final BiConsumer<Float, Float> mSetExpandedHeight = this::setExpandedHeight;
+ private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction;
+ private final KeyguardBypassController mBypassController;
+ private final StatusBarStateController mStatusBarStateController;
+ private final CommandQueue mCommandQueue;
@VisibleForTesting
float mExpandedHeight;
@VisibleForTesting
boolean mIsExpanded;
@VisibleForTesting
- float mExpandFraction;
+ float mAppearFraction;
private ExpandableNotificationRow mTrackedChild;
private boolean mShown;
private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener =
@@ -77,13 +87,17 @@
};
private boolean mAnimationsEnabled = true;
Point mPoint;
+ private KeyguardMonitor mKeyguardMonitor;
public HeadsUpAppearanceController(
NotificationIconAreaController notificationIconAreaController,
HeadsUpManagerPhone headsUpManager,
- View statusbarView) {
- this(notificationIconAreaController, headsUpManager,
+ View statusbarView,
+ SysuiStatusBarStateController statusBarStateController,
+ KeyguardBypassController keyguardBypassController) {
+ this(notificationIconAreaController, headsUpManager, statusBarStateController,
+ keyguardBypassController,
statusbarView.findViewById(R.id.heads_up_status_bar_view),
statusbarView.findViewById(R.id.notification_stack_scroller),
statusbarView.findViewById(R.id.notification_panel),
@@ -96,6 +110,8 @@
public HeadsUpAppearanceController(
NotificationIconAreaController notificationIconAreaController,
HeadsUpManagerPhone headsUpManager,
+ StatusBarStateController stateController,
+ KeyguardBypassController bypassController,
HeadsUpStatusBarView headsUpStatusBarView,
NotificationStackScrollLayout stackScroller,
NotificationPanelView panelView,
@@ -114,7 +130,7 @@
panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
panelView.addVerticalTranslationListener(mUpdatePanelTranslation);
panelView.setHeadsUpAppearanceController(this);
- mStackScroller.addOnExpandedHeightListener(mSetExpandedHeight);
+ mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
mStackScroller.setHeadsUpAppearanceController(this);
mClockView = clockView;
@@ -135,6 +151,10 @@
mHeadsUpStatusBarView.removeOnLayoutChangeListener(this);
}
});
+ mBypassController = bypassController;
+ mStatusBarStateController = stateController;
+ mCommandQueue = getComponent(headsUpStatusBarView.getContext(), CommandQueue.class);
+ mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
}
@@ -144,7 +164,7 @@
mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation);
mPanelView.setHeadsUpAppearanceController(null);
- mStackScroller.removeOnExpandedHeightListener(mSetExpandedHeight);
+ mStackScroller.removeOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
mDarkIconDispatcher.removeDarkReceiver(this);
}
@@ -219,7 +239,7 @@
private void updateTopEntry() {
NotificationEntry newEntry = null;
- if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
+ if (shouldBeVisible()) {
newEntry = mHeadsUpManager.getTopEntry();
}
NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
@@ -342,7 +362,13 @@
* @return if the heads up status bar view should be shown
*/
public boolean shouldBeVisible() {
- return !mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp();
+ boolean canShow = !mIsExpanded;
+ if (mBypassController.getBypassEnabled() &&
+ (mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+ || mKeyguardMonitor.isKeyguardGoingAway())) {
+ canShow = true;
+ }
+ return canShow && mHeadsUpManager.hasPinnedHeadsUp();
}
@Override
@@ -351,12 +377,24 @@
updateHeader(entry);
}
- public void setExpandedHeight(float expandedHeight, float appearFraction) {
- boolean changedHeight = expandedHeight != mExpandedHeight;
+ @Override
+ public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
+ if (mStatusBarStateController.getState() != StatusBarState.SHADE) {
+ // Show the status bar icons when the pinned mode changes
+ mCommandQueue.recomputeDisableFlags(
+ mHeadsUpStatusBarView.getContext().getDisplayId(), false);
+ }
+ }
+
+ public void setAppearFraction(float expandedHeight, float appearFraction) {
+ boolean changed = expandedHeight != mExpandedHeight;
mExpandedHeight = expandedHeight;
- mExpandFraction = appearFraction;
+ mAppearFraction = appearFraction;
boolean isExpanded = expandedHeight > 0;
- if (changedHeight) {
+ // We only notify if the expandedHeight changed and not on the appearFraction, since
+ // otherwise we may run into an infinite loop where the panel and this are constantly
+ // updating themselves over just a small fraction
+ if (changed) {
updateHeadsUpHeaders();
}
if (isExpanded != mIsExpanded) {
@@ -389,8 +427,9 @@
public void updateHeader(NotificationEntry entry) {
ExpandableNotificationRow row = entry.getRow();
float headerVisibleAmount = 1.0f;
- if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
- headerVisibleAmount = mExpandFraction;
+ if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild
+ || row.showingPulsing()) {
+ headerVisibleAmount = mAppearFraction;
}
row.setHeaderVisibleAmount(headerVisibleAmount);
}
@@ -400,8 +439,7 @@
mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint);
}
- public void setPublicMode(boolean publicMode) {
- mHeadsUpStatusBarView.setPublicMode(publicMode);
+ public void onStateChanged() {
updateTopEntry();
}
@@ -410,7 +448,7 @@
mTrackedChild = oldController.mTrackedChild;
mExpandedHeight = oldController.mExpandedHeight;
mIsExpanded = oldController.mIsExpanded;
- mExpandFraction = oldController.mExpandFraction;
+ mAppearFraction = oldController.mAppearFraction;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index bec655c..ade855e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -66,6 +66,7 @@
@VisibleForTesting
final int mExtensionTime;
private final StatusBarStateController mStatusBarStateController;
+ private final KeyguardBypassController mBypassController;
private View mStatusBarWindowView;
private NotificationGroupManager mGroupManager;
private VisualStabilityManager mVisualStabilityManager;
@@ -113,7 +114,8 @@
@Inject
public HeadsUpManagerPhone(@NonNull final Context context,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ KeyguardBypassController bypassController) {
super(context);
Resources resources = mContext.getResources();
mAutoDismissNotificationDecayDozing = resources.getInteger(
@@ -121,6 +123,7 @@
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
mStatusBarStateController = statusBarStateController;
mStatusBarStateController.addCallback(this);
+ mBypassController = bypassController;
initResources();
}
@@ -231,6 +234,17 @@
mStatusBarState = newState;
}
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ if (!isDozing) {
+ // Let's make sure all huns we got while dozing time out within the normal timeout
+ // duration. Otherwise they could get stuck for a very long time
+ for (AlertEntry entry : mAlertEntries.values()) {
+ entry.updateEntry(true /* updatePostTime */);
+ }
+ }
+ }
+
/**
* Set that we are exiting the headsUp pinned mode, but some notifications might still be
* animating out. This is used to keep the touchable regions in a sane state.
@@ -412,8 +426,11 @@
@Override
protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) {
- return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
- || super.shouldHeadsUpBecomePinned(entry);
+ boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsExpanded;
+ if (mBypassController.getBypassEnabled()) {
+ pin |= mStatusBarState == StatusBarState.KEYGUARD;
+ }
+ return pin || super.shouldHeadsUpBecomePinned(entry);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 58d1462..3f1cb6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone
import android.content.Context
+import android.content.pm.PackageManager
import android.hardware.biometrics.BiometricSourceType
import android.hardware.face.FaceManager
import android.provider.Settings
@@ -73,6 +74,9 @@
}
}
})
+ if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ return
+ }
val faceManager = context.getSystemService(FaceManager::class.java)
if (faceManager?.isHardwareDetected != true) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 7b1d1c6..0bbfbef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -435,18 +435,6 @@
}
@Override
- public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
- }
-
- @Override
- public void onHeadsUpPinned(NotificationEntry entry) {
- }
-
- @Override
- public void onHeadsUpUnPinned(NotificationEntry entry) {
- }
-
- @Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
onAlertStateChanged(entry, isHeadsUp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 6f9a6ec..cd97722 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -13,7 +13,6 @@
import androidx.annotation.NonNull;
import androidx.collection.ArrayMap;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.settingslib.Utils;
@@ -24,7 +23,6 @@
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -242,11 +240,15 @@
protected boolean shouldShowNotificationIcon(NotificationEntry entry,
boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon,
- boolean hidePulsing) {
+ boolean hidePulsing, boolean onlyShowCenteredIcon) {
- final boolean isCenteredNotificationIcon = entry.centeredIcon != null
+ final boolean isCenteredNotificationIcon = mCenteredIconView != null
+ && entry.centeredIcon != null
&& Objects.equals(entry.centeredIcon, mCenteredIconView);
- if (hideCenteredIcon == isCenteredNotificationIcon) {
+ if (onlyShowCenteredIcon) {
+ return isCenteredNotificationIcon;
+ }
+ if (hideCenteredIcon && isCenteredNotificationIcon) {
return false;
}
if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
@@ -299,8 +301,9 @@
false /* hideDismissed */,
false /* hideRepliedMessages */,
false /* hideCurrentMedia */,
- true /* hide centered icon */,
- false /* hidePulsing */);
+ false /* hide centered icon */,
+ false /* hidePulsing */,
+ false /* onlyShowCenteredIcon */);
}
public void updateStatusBarIcons() {
@@ -311,7 +314,8 @@
true /* hideRepliedMessages */,
false /* hideCurrentMedia */,
true /* hide centered icon */,
- false /* hidePulsing */);
+ false /* hidePulsing */,
+ false /* onlyShowCenteredIcon */);
}
private void updateCenterIcon() {
@@ -322,7 +326,8 @@
false /* hideRepliedMessages */,
false /* hideCurrentMedia */,
false /* hide centered icon */,
- false /* hidePulsing */);
+ false /* hidePulsing */,
+ true/* onlyShowCenteredIcon */);
}
public void updateAodNotificationIcons() {
@@ -333,7 +338,8 @@
true /* hideRepliedMessages */,
true /* hideCurrentMedia */,
true /* hide centered icon */,
- mBypassController.getBypassEnabled() /* hidePulsing */);
+ mBypassController.getBypassEnabled() /* hidePulsing */,
+ false /* onlyShowCenteredIcon */);
}
/**
@@ -349,7 +355,7 @@
private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,
NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia,
- boolean hideCenteredIcon, boolean hidePulsing) {
+ boolean hideCenteredIcon, boolean hidePulsing, boolean onlyShowCenteredIcon) {
ArrayList<StatusBarIconView> toShow = new ArrayList<>(
mNotificationScrollLayout.getChildCount());
@@ -359,7 +365,8 @@
if (view instanceof ExpandableNotificationRow) {
NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();
if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
- hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing)) {
+ hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing,
+ onlyShowCenteredIcon)) {
StatusBarIconView iconView = function.apply(ent);
if (iconView != null) {
toShow.add(iconView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 4acf7b2..a53ce9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -320,7 +320,7 @@
if (child instanceof StatusBarIconView) {
boolean isReplacingIcon = isReplacingIcon(child);
final StatusBarIconView icon = (StatusBarIconView) child;
- if (mAnimationsEnabled && icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
+ if (areAnimationsEnabled(icon) && icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
&& child.getVisibility() == VISIBLE && isReplacingIcon) {
int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX());
if (mAddAnimationStartIndex < 0) {
@@ -331,7 +331,7 @@
}
if (!mChangingViewPositions) {
mIconStates.remove(child);
- if (mAnimationsEnabled && !isReplacingIcon) {
+ if (areAnimationsEnabled(icon) && !isReplacingIcon) {
addTransientView(icon, 0);
boolean isIsolatedIcon = child == mIsolatedIcon;
icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */,
@@ -342,6 +342,10 @@
}
}
+ private boolean areAnimationsEnabled(StatusBarIconView icon) {
+ return mAnimationsEnabled || icon == mIsolatedIcon;
+ }
+
/**
* Finds the first view with a translation bigger then a given value
*/
@@ -695,7 +699,7 @@
StatusBarIconView icon = (StatusBarIconView) view;
boolean animate = false;
AnimationProperties animationProperties = null;
- boolean animationsAllowed = mAnimationsEnabled && !mDisallowNextAnimation
+ boolean animationsAllowed = areAnimationsEnabled(icon) && !mDisallowNextAnimation
&& !noAnimations;
if (animationsAllowed) {
if (justAdded || justReplaced) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 64a8ce0..32dc96d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -148,6 +148,15 @@
private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ private static final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT
+ = AnimatableProperty.from("KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
+ NotificationPanelView::setKeyguardHeadsUpShowingAmount,
+ NotificationPanelView::getKeyguardHeadsUpShowingAmount,
+ R.id.keyguard_hun_animator_tag,
+ R.id.keyguard_hun_animator_end_tag,
+ R.id.keyguard_hun_animator_start_tag);
+ private static final AnimationProperties KEYGUARD_HUN_PROPERTIES =
+ new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
private final InjectionInflationController mInjectionInflationController;
private final PowerManager mPowerManager;
@@ -356,6 +365,9 @@
private Runnable mOnReinflationListener;
private int mDarkIconSize;
private int mHeadsUpInset;
+ private boolean mHeadsUpPinnedMode;
+ private float mKeyguardHeadsUpShowingAmount = 0.0f;
+ private boolean mShowingKeyguardHeadsUp;
@Inject
public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -741,7 +753,6 @@
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
}
mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
- mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
mStackScrollerMeasuringPass++;
@@ -1415,6 +1426,7 @@
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == StatusBarState.KEYGUARD) {
animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mNotificationStackScroller.resetScrollPosition();
// Only animate header if the header is visible. If not, it will partially animate out
// the top of QS
if (!mQsExpanded) {
@@ -1429,6 +1441,7 @@
}
}
}
+ updateKeyguardStatusBarForHeadsUp();
if (keyguardShowing) {
updateDozingVisibilities(false /* animate */);
}
@@ -1721,21 +1734,11 @@
} else {
int expandedPosition = mClockPositionResult.stackScrollerPadding;
return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
- calculateHeaderAppearAmountBypass());
+ mNotificationStackScroller.calculateAppearFractionBypass());
}
}
- private float calculateHeaderAppearAmountBypass() {
- float pulseHeight = mNotificationStackScroller.getPulseHeight();
- float wakeUpHeight = mNotificationStackScroller.getWakeUpHeight();
- float dragDownAmount = pulseHeight - wakeUpHeight;
-
- // The total distance required to fully reveal the header
- float totalDistance = mClockPositionResult.stackScrollerPadding;
- return MathUtils.smoothStep(0, totalDistance, dragDownAmount);
- }
-
protected void requestScrollerTopPaddingUpdate(boolean animate) {
mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate);
if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
@@ -2029,7 +2032,8 @@
!mHeadsUpManager.hasPinnedHeadsUp()) {
alpha = getFadeoutAlpha();
}
- if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning) {
+ if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning
+ && !mKeyguardBypassController.getBypassEnabled()) {
alpha *= mClockPositionResult.clockAlpha;
}
mNotificationStackScroller.setAlpha(alpha);
@@ -2067,7 +2071,7 @@
if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
return -mQs.getQsMinExpansionHeight();
}
- float appearAmount = mNotificationStackScroller.getAppearFraction(mExpandedHeight);
+ float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight);
float startHeight = -mQsExpansionHeight;
if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
&& mNotificationStackScroller.isPulseExpanding()) {
@@ -2077,7 +2081,7 @@
// again after the header has animated away
appearAmount = 0;
} else {
- appearAmount = calculateHeaderAppearAmountBypass();
+ appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
}
startHeight = -mQs.getQsMinExpansionHeight();
}
@@ -2118,6 +2122,7 @@
float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
* mKeyguardStatusBarAnimateAlpha;
+ newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
mKeyguardStatusBar.setAlpha(newAlpha);
mKeyguardStatusBar.setVisibility(newAlpha != 0f && !mDozing ? VISIBLE : INVISIBLE);
}
@@ -2750,16 +2755,51 @@
mHeadsUpExistenceChangedRunnable);
}
updateGestureExclusionRect();
+ mHeadsUpPinnedMode = inPinnedMode;
+ updateHeadsUpVisibility();
+ updateKeyguardStatusBarForHeadsUp();
+ }
+
+ private void updateKeyguardStatusBarForHeadsUp() {
+ boolean showingKeyguardHeadsUp = mKeyguardShowing
+ && mHeadsUpAppearanceController.shouldBeVisible();
+ if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
+ mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
+ if (mKeyguardShowing) {
+ PropertyAnimator.setProperty(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
+ showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
+ true /* animate */);
+ } else {
+ PropertyAnimator.applyImmediately(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
+ }
+ }
+ }
+
+ private void setKeyguardHeadsUpShowingAmount(float amount) {
+ mKeyguardHeadsUpShowingAmount = amount;
+ updateHeaderKeyguardAlpha();
+ }
+
+ private float getKeyguardHeadsUpShowingAmount() {
+ return mKeyguardHeadsUpShowingAmount;
}
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
mHeadsUpAnimatingAway = headsUpAnimatingAway;
mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+ updateHeadsUpVisibility();
+ }
+
+ private void updateHeadsUpVisibility() {
+ ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
}
@Override
public void onHeadsUpPinned(NotificationEntry entry) {
- mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
+ if (!isOnKeyguard()) {
+ mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(),
+ true);
+ }
}
@Override
@@ -2768,7 +2808,7 @@
// When we're unpinning the notification via active edge they remain heads-upped,
// we need to make sure that an animation happens in this case, otherwise the notification
// will stick to the top without any interaction.
- if (isFullyCollapsed() && entry.isRowHeadsUp()) {
+ if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
mNotificationStackScroller.generateHeadsUpAnimation(
entry.getHeadsUpAnimationView(), false);
entry.setHeadsUpIsVisible();
@@ -2835,7 +2875,7 @@
}
protected void setHorizontalPanelTranslation(float translation) {
- mNotificationStackScroller.setHorizontalPanelTranslation(translation);
+ mNotificationStackScroller.setTranslationX(translation);
mQsFrame.setTranslationX(translation);
int size = mVerticalTranslationListener.size();
for (int i = 0; i < size; i++) {
@@ -2891,7 +2931,8 @@
@Override
protected boolean isPanelVisibleBecauseOfHeadsUp() {
- return mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway;
+ return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
+ && mBarState == StatusBarState.SHADE;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 65b0ecc..063d00b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -107,8 +107,12 @@
return mExpanded;
}
- private void updateVisibility() {
- mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
+ protected void updateVisibility() {
+ mPanel.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
+ }
+
+ protected boolean shouldPanelBeVisible() {
+ return mExpanded || mBouncerShowing;
}
public boolean panelEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 68eba50..660810f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -46,6 +46,7 @@
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.Objects;
@@ -82,6 +83,7 @@
* Draw this many pixels into the left/right side of the cutout to optimally use the space
*/
private int mCutoutSideNudge = 0;
+ private boolean mHeadsUpVisible;
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -379,4 +381,14 @@
}
return null;
}
+
+ public void setHeadsUpVisible(boolean headsUpVisible) {
+ mHeadsUpVisible = headsUpVisible;
+ updateVisibility();
+ }
+
+ @Override
+ protected boolean shouldPanelBeVisible() {
+ return mHeadsUpVisible || super.shouldPanelBeVisible();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 213475f..7db4afc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -847,7 +847,8 @@
mHeadsUpAppearanceController.destroy();
}
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
- mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
+ mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow,
+ mStatusBarStateController, mKeyguardBypassController);
mHeadsUpAppearanceController.readFrom(oldController);
mStatusBarWindow.setStatusBarView(mStatusBarView);
updateAreThereNotifications();
@@ -1537,10 +1538,16 @@
});
}
} else {
- if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()) {
+ boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
+ && mState == StatusBarState.KEYGUARD;
+ if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()
+ || bypassKeyguard) {
// We are currently tracking or is open and the shade doesn't need to be kept
// open artificially.
mStatusBarWindowController.setHeadsUpShowing(false);
+ if (bypassKeyguard) {
+ mStatusBarWindowController.setForceStatusBarVisible(false);
+ }
} else {
// we need to keep the panel open artificially, let's wait until the animation
// is finished.
@@ -1557,20 +1564,12 @@
}
@Override
- public void onHeadsUpPinned(NotificationEntry entry) {
- dismissVolumeDialog();
- }
-
- @Override
- public void onHeadsUpUnPinned(NotificationEntry entry) {
- }
-
- @Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
mEntryManager.updateNotifications();
- if (isDozing()) {
- if (isHeadsUp) {
- mDozeServiceHost.fireNotificationPulse();
+ if (isDozing() && isHeadsUp) {
+ mDozeServiceHost.fireNotificationPulse();
+ if (mPulsing) {
+ mDozeScrimController.cancelPendingPulseTimeout();
}
}
if (!isHeadsUp && !mHeadsUpManager.hasNotifications()) {
@@ -1671,7 +1670,7 @@
}
}
- public boolean isHeadsUpShouldBeVisible() {
+ public boolean headsUpShouldBeVisible() {
return mHeadsUpAppearanceController.shouldBeVisible();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
index bebc20b..775a3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
@@ -31,6 +31,11 @@
static final String REASON_WRAP = "wrap";
/**
+ * Default wake-lock timeout, to avoid battery regressions.
+ */
+ long DEFAULT_MAX_TIMEOUT = 20000;
+
+ /**
* @param why A tag that will be saved for sysui dumps.
* @see android.os.PowerManager.WakeLock#acquire()
**/
@@ -46,7 +51,14 @@
Runnable wrap(Runnable r);
static WakeLock createPartial(Context context, String tag) {
- return wrap(createPartialInner(context, tag));
+ return createPartial(context, tag, DEFAULT_MAX_TIMEOUT);
+ }
+
+ /**
+ * Creates a {@link WakeLock} that has a default release timeout.
+ * @see android.os.PowerManager.WakeLock#acquire(long) */
+ static WakeLock createPartial(Context context, String tag, long maxTimeout) {
+ return wrap(createPartialInner(context, tag), maxTimeout);
}
@VisibleForTesting
@@ -66,7 +78,14 @@
};
}
- static WakeLock wrap(final PowerManager.WakeLock inner) {
+ /**
+ * Create a {@link WakeLock} containing a {@link PowerManager.WakeLock}.
+ * @param inner To be wrapped.
+ * @param maxTimeout When to expire.
+ * @return The new wake lock.
+ */
+ @VisibleForTesting
+ static WakeLock wrap(final PowerManager.WakeLock inner, long maxTimeout) {
return new WakeLock() {
private final HashMap<String, Integer> mActiveClients = new HashMap<>();
@@ -74,7 +93,7 @@
public void acquire(String why) {
mActiveClients.putIfAbsent(why, 0);
mActiveClients.put(why, mActiveClients.get(why) + 1);
- inner.acquire();
+ inner.acquire(maxTimeout);
}
/** @see PowerManager.WakeLock#release() */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
new file mode 100644
index 0000000..d011e48
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ClassifierTest extends SysuiTestCase {
+
+ private FalsingDataProvider mDataProvider;
+ private List<MotionEvent> mMotionEvents = new ArrayList<>();
+ private float mOffsetX = 0;
+ private float mOffsetY = 0;
+
+ @Before
+ public void setup() {
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ displayMetrics.xdpi = 100;
+ displayMetrics.ydpi = 100;
+ displayMetrics.widthPixels = 1000;
+ displayMetrics.heightPixels = 1000;
+ mDataProvider = new FalsingDataProvider(displayMetrics);
+ }
+
+ @After
+ public void tearDown() {
+ resetDataProvider();
+ }
+
+ FalsingDataProvider getDataProvider() {
+ return mDataProvider;
+ }
+
+ void setOffsetX(float offsetX) {
+ mOffsetX = offsetX;
+ }
+
+ void setOffsetY(float offsetY) {
+ mOffsetY = offsetY;
+ }
+
+ void resetDataProvider() {
+ for (MotionEvent motionEvent : mMotionEvents) {
+ motionEvent.recycle();
+ }
+
+ mMotionEvents.clear();
+
+ mDataProvider.onSessionEnd();
+ }
+
+ MotionEvent appendDownEvent(float x, float y) {
+ return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y);
+ }
+
+ MotionEvent appendDownEvent(float x, float y, long eventTime) {
+ return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y, eventTime);
+ }
+
+ MotionEvent appendMoveEvent(float x, float y) {
+ return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y);
+ }
+
+ MotionEvent appendMoveEvent(float x, float y, long eventTime) {
+ return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y, eventTime);
+ }
+
+
+ MotionEvent appendUpEvent(float x, float y) {
+ return appendMotionEvent(MotionEvent.ACTION_UP, x, y);
+ }
+
+ MotionEvent appendUpEvent(float x, float y, long eventTime) {
+ return appendMotionEvent(MotionEvent.ACTION_UP, x, y, eventTime);
+ }
+
+ private MotionEvent appendMotionEvent(int actionType, float x, float y) {
+
+ long eventTime = mMotionEvents.isEmpty() ? 1 : mMotionEvents.get(
+ mMotionEvents.size() - 1).getEventTime() + 1;
+ return appendMotionEvent(actionType, x, y, eventTime);
+ }
+
+ private MotionEvent appendMotionEvent(int actionType, float x, float y, long eventTime) {
+ x += mOffsetX;
+ y += mOffsetY;
+
+ MotionEvent motionEvent = MotionEvent.obtain(1, eventTime, actionType, x, y,
+ 0);
+ mMotionEvents.add(motionEvent);
+
+ mDataProvider.onMotionEvent(motionEvent);
+
+ return motionEvent;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
index ade5f36..b45d3f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
@@ -28,8 +28,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,7 +38,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class DiagonalClassifierTest extends SysuiTestCase {
+public class DiagonalClassifierTest extends ClassifierTest {
// Next variable is not actually five, but is very close. 5 degrees is currently the value
// used in the diagonal classifier, so we want slightly less than that to deal with
@@ -57,10 +56,16 @@
@Before
public void setup() {
+ super.setup();
MockitoAnnotations.initMocks(this);
mClassifier = new DiagonalClassifier(mDataProvider);
}
+ @After
+ public void tearDown() {
+ super.tearDown();
+ }
+
@Test
public void testPass_UnknownAngle() {
when(mDataProvider.getAngle()).thenReturn(Float.MAX_VALUE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
index 3d0471b..805bb91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
@@ -18,48 +18,37 @@
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class DistanceClassifierTest extends SysuiTestCase {
+public class DistanceClassifierTest extends ClassifierTest {
- @Mock
private FalsingDataProvider mDataProvider;
private FalsingClassifier mClassifier;
- private List<MotionEvent> mMotionEvents = new ArrayList<>();
-
- private static final float DPI = 100;
- private static final int SCREEN_SIZE = (int) (DPI * 10);
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
- when(mDataProvider.getHeightPixels()).thenReturn(SCREEN_SIZE);
- when(mDataProvider.getWidthPixels()).thenReturn(SCREEN_SIZE);
- when(mDataProvider.getXdpi()).thenReturn(DPI);
- when(mDataProvider.getYdpi()).thenReturn(DPI);
+ super.setup();
+ mDataProvider = getDataProvider();
mClassifier = new DistanceClassifier(mDataProvider);
}
+ @After
+ public void tearDown() {
+ super.tearDown();
+ }
+
@Test
public void testPass_noPointer() {
assertThat(mClassifier.isFalseTouch(), is(true));
@@ -67,104 +56,54 @@
@Test
public void testPass_fling() {
- MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
- MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 2, 0);
- MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_UP, 1, 40, 0);
- appendMotionEvent(motionEventA);
+ mClassifier.onTouchEvent(appendDownEvent(1, 1));
assertThat(mClassifier.isFalseTouch(), is(true));
- appendMotionEvent(motionEventB);
+ mClassifier.onTouchEvent(appendMoveEvent(1, 2));
assertThat(mClassifier.isFalseTouch(), is(true));
- appendMotionEvent(motionEventC);
+ mClassifier.onTouchEvent(appendUpEvent(1, 40));
assertThat(mClassifier.isFalseTouch(), is(false));
-
- motionEventA.recycle();
- motionEventB.recycle();
- motionEventC.recycle();
}
@Test
public void testFail_flingShort() {
- MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
- MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 2, 0);
- MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_UP, 1, 10, 0);
-
- appendMotionEvent(motionEventA);
+ mClassifier.onTouchEvent(appendDownEvent(1, 1));
assertThat(mClassifier.isFalseTouch(), is(true));
- appendMotionEvent(motionEventB);
+ mClassifier.onTouchEvent(appendMoveEvent(1, 2));
assertThat(mClassifier.isFalseTouch(), is(true));
- appendMotionEvent(motionEventC);
+ mClassifier.onTouchEvent(appendUpEvent(1, 10));
assertThat(mClassifier.isFalseTouch(), is(true));
-
- motionEventA.recycle();
- motionEventB.recycle();
- motionEventC.recycle();
}
@Test
public void testFail_flingSlowly() {
// These events, in testing, result in a fling that falls just short of the threshold.
- MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
- MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 15, 0);
- MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_MOVE, 1, 16, 0);
- MotionEvent motionEventD = MotionEvent.obtain(1, 300, MotionEvent.ACTION_MOVE, 1, 17, 0);
- MotionEvent motionEventE = MotionEvent.obtain(1, 301, MotionEvent.ACTION_MOVE, 1, 18, 0);
- MotionEvent motionEventF = MotionEvent.obtain(1, 500, MotionEvent.ACTION_UP, 1, 19, 0);
- appendMotionEvent(motionEventA);
+ mClassifier.onTouchEvent(appendDownEvent(1, 1, 1));
assertThat(mClassifier.isFalseTouch(), is(true));
- appendMotionEvent(motionEventB);
+ mClassifier.onTouchEvent(appendMoveEvent(1, 15, 2));
assertThat(mClassifier.isFalseTouch(), is(true));
- appendMotionEvent(motionEventC);
- appendMotionEvent(motionEventD);
- appendMotionEvent(motionEventE);
- appendMotionEvent(motionEventF);
+ mClassifier.onTouchEvent(appendMoveEvent(1, 16, 3));
+ mClassifier.onTouchEvent(appendMoveEvent(1, 17, 300));
+ mClassifier.onTouchEvent(appendMoveEvent(1, 18, 301));
+ mClassifier.onTouchEvent(appendUpEvent(1, 19, 501));
assertThat(mClassifier.isFalseTouch(), is(true));
-
- motionEventA.recycle();
- motionEventB.recycle();
- motionEventC.recycle();
- motionEventD.recycle();
- motionEventE.recycle();
- motionEventF.recycle();
}
@Test
public void testPass_swipe() {
- MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
- MotionEvent motionEventB = MotionEvent.obtain(1, 3, MotionEvent.ACTION_MOVE, 1, DPI * 3, 0);
- MotionEvent motionEventC = MotionEvent.obtain(1, 1000, MotionEvent.ACTION_UP, 1, DPI * 3,
- 0);
- appendMotionEvent(motionEventA);
+ mClassifier.onTouchEvent(appendDownEvent(1, 1));
assertThat(mClassifier.isFalseTouch(), is(true));
-
- appendMotionEvent(motionEventB);
- appendMotionEvent(motionEventC);
+ mClassifier.onTouchEvent(appendMoveEvent(1, mDataProvider.getYdpi() * 3, 3));
+ mClassifier.onTouchEvent(appendUpEvent(1, mDataProvider.getYdpi() * 3, 300));
assertThat(mClassifier.isFalseTouch(), is(false));
-
- motionEventA.recycle();
- motionEventB.recycle();
- motionEventC.recycle();
- }
-
- private void appendMotionEvent(MotionEvent motionEvent) {
- if (mMotionEvents.isEmpty()) {
- when(mDataProvider.getFirstRecentMotionEvent()).thenReturn(motionEvent);
- }
-
- mMotionEvents.add(motionEvent);
- when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents);
-
- when(mDataProvider.getLastMotionEvent()).thenReturn(motionEvent);
-
- mClassifier.onTouchEvent(motionEvent);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
index 1da4206..748c137 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
@@ -22,12 +22,12 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.DisplayMetrics;
import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,24 +37,32 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class FalsingDataProviderTest extends SysuiTestCase {
+public class FalsingDataProviderTest extends ClassifierTest {
private FalsingDataProvider mDataProvider;
@Before
public void setup() {
- mDataProvider = new FalsingDataProvider(getContext());
+ super.setup();
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ displayMetrics.xdpi = 100;
+ displayMetrics.ydpi = 100;
+ displayMetrics.widthPixels = 1000;
+ displayMetrics.heightPixels = 1000;
+ mDataProvider = new FalsingDataProvider(displayMetrics);
+ }
+
+ @After
+ public void tearDown() {
+ super.tearDown();
+ mDataProvider.onSessionEnd();
}
@Test
public void test_trackMotionEvents() {
- MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 2, 9);
- MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 4, 7);
- MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_UP, 3, 6, 5);
-
- mDataProvider.onMotionEvent(motionEventA);
- mDataProvider.onMotionEvent(motionEventB);
- mDataProvider.onMotionEvent(motionEventC);
+ mDataProvider.onMotionEvent(appendDownEvent(2, 9));
+ mDataProvider.onMotionEvent(appendMoveEvent(4, 7));
+ mDataProvider.onMotionEvent(appendUpEvent(6, 5));
List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
assertThat(motionEventList.size(), is(3));
@@ -70,20 +78,12 @@
assertThat(motionEventList.get(0).getY(), is(9f));
assertThat(motionEventList.get(1).getY(), is(7f));
assertThat(motionEventList.get(2).getY(), is(5f));
-
- motionEventA.recycle();
- motionEventB.recycle();
- motionEventC.recycle();
}
@Test
public void test_trackRecentMotionEvents() {
- MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 2, 9);
- MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 800, 4, 7);
- MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_UP, 1200, 6, 5);
-
- mDataProvider.onMotionEvent(motionEventA);
- mDataProvider.onMotionEvent(motionEventB);
+ mDataProvider.onMotionEvent(appendDownEvent(2, 9, 1));
+ mDataProvider.onMotionEvent(appendMoveEvent(4, 7, 800));
List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
assertThat(motionEventList.size(), is(2));
@@ -96,7 +96,7 @@
assertThat(motionEventList.get(0).getY(), is(9f));
assertThat(motionEventList.get(1).getY(), is(7f));
- mDataProvider.onMotionEvent(motionEventC);
+ mDataProvider.onMotionEvent(appendUpEvent(6, 5, 1200));
// Still two events, but event a is gone.
assertThat(motionEventList.size(), is(2));
@@ -115,18 +115,14 @@
assertThat(firstRealMotionEvent.getEventTime(), is(1L));
assertThat(firstRealMotionEvent.getX(), is(2f));
assertThat(firstRealMotionEvent.getY(), is(9f));
-
- motionEventA.recycle();
- motionEventB.recycle();
- motionEventC.recycle();
}
@Test
public void test_unpackMotionEvents() {
// Batching only works for motion events of the same type.
- MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 1, 2, 9);
- MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 4, 7);
- MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 3, 6, 5);
+ MotionEvent motionEventA = appendMoveEvent(2, 9);
+ MotionEvent motionEventB = appendMoveEvent(4, 7);
+ MotionEvent motionEventC = appendMoveEvent(6, 5);
motionEventA.addBatch(motionEventB);
motionEventA.addBatch(motionEventC);
// Note that calling addBatch changes properties on the original event, not just it's
@@ -148,114 +144,86 @@
assertThat(motionEventList.get(0).getY(), is(9f));
assertThat(motionEventList.get(1).getY(), is(7f));
assertThat(motionEventList.get(2).getY(), is(5f));
-
- motionEventA.recycle();
- motionEventB.recycle();
- motionEventC.recycle();
}
@Test
public void test_getAngle() {
- MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+ MotionEvent motionEventOrigin = appendDownEvent(0, 0);
- MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventA);
+ mDataProvider.onMotionEvent(appendMoveEvent(1, 1));
assertThat((double) mDataProvider.getAngle(), closeTo(Math.PI / 4, .001));
- motionEventA.recycle();
mDataProvider.onSessionEnd();
- MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -1, -1);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventB);
+ mDataProvider.onMotionEvent(appendMoveEvent(-1, -1));
assertThat((double) mDataProvider.getAngle(), closeTo(5 * Math.PI / 4, .001));
- motionEventB.recycle();
mDataProvider.onSessionEnd();
- MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 2, 0);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventC);
+ mDataProvider.onMotionEvent(appendMoveEvent(2, 0));
assertThat((double) mDataProvider.getAngle(), closeTo(0, .001));
- motionEventC.recycle();
mDataProvider.onSessionEnd();
}
@Test
public void test_isHorizontal() {
- MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+ MotionEvent motionEventOrigin = appendDownEvent(0, 0);
- MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventA);
+ mDataProvider.onMotionEvent(appendMoveEvent(1, 1));
assertThat(mDataProvider.isHorizontal(), is(false));
- motionEventA.recycle();
mDataProvider.onSessionEnd();
- MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 2, 1);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventB);
+ mDataProvider.onMotionEvent(appendMoveEvent(2, 1));
assertThat(mDataProvider.isHorizontal(), is(true));
- motionEventB.recycle();
mDataProvider.onSessionEnd();
- MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -1);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventC);
+ mDataProvider.onMotionEvent(appendMoveEvent(-3, -1));
assertThat(mDataProvider.isHorizontal(), is(true));
- motionEventC.recycle();
mDataProvider.onSessionEnd();
}
@Test
public void test_isVertical() {
- MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+ MotionEvent motionEventOrigin = appendDownEvent(0, 0);
- MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 0);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventA);
+ mDataProvider.onMotionEvent(appendMoveEvent(1, 0));
assertThat(mDataProvider.isVertical(), is(false));
- motionEventA.recycle();
mDataProvider.onSessionEnd();
- MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 1);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventB);
+ mDataProvider.onMotionEvent(appendMoveEvent(0, 1));
assertThat(mDataProvider.isVertical(), is(true));
- motionEventB.recycle();
mDataProvider.onSessionEnd();
- MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -10);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventC);
+ mDataProvider.onMotionEvent(appendMoveEvent(-3, -10));
assertThat(mDataProvider.isVertical(), is(true));
- motionEventC.recycle();
mDataProvider.onSessionEnd();
}
@Test
public void test_isRight() {
- MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+ MotionEvent motionEventOrigin = appendDownEvent(0, 0);
- MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventA);
+ mDataProvider.onMotionEvent(appendMoveEvent(1, 1));
assertThat(mDataProvider.isRight(), is(true));
- motionEventA.recycle();
mDataProvider.onSessionEnd();
- MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 1);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventB);
+ mDataProvider.onMotionEvent(appendMoveEvent(0, 1));
assertThat(mDataProvider.isRight(), is(false));
- motionEventB.recycle();
mDataProvider.onSessionEnd();
- MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -10);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventC);
+ mDataProvider.onMotionEvent(appendMoveEvent(-3, -10));
assertThat(mDataProvider.isRight(), is(false));
- motionEventC.recycle();
mDataProvider.onSessionEnd();
}
@@ -263,31 +231,21 @@
public void test_isUp() {
// Remember that our y axis is flipped.
- MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+ MotionEvent motionEventOrigin = appendDownEvent(0, 0);
- MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, -1);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventA);
+ mDataProvider.onMotionEvent(appendMoveEvent(1, -1));
assertThat(mDataProvider.isUp(), is(true));
- motionEventA.recycle();
mDataProvider.onSessionEnd();
- MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 0);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventB);
+ mDataProvider.onMotionEvent(appendMoveEvent(0, 0));
assertThat(mDataProvider.isUp(), is(false));
- motionEventB.recycle();
mDataProvider.onSessionEnd();
- MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, 10);
mDataProvider.onMotionEvent(motionEventOrigin);
- mDataProvider.onMotionEvent(motionEventC);
+ mDataProvider.onMotionEvent(appendMoveEvent(-3, 10));
assertThat(mDataProvider.isUp(), is(false));
- motionEventC.recycle();
mDataProvider.onSessionEnd();
}
-
- private MotionEvent obtainMotionEvent(int action, long eventTimeMs, float x, float y) {
- return MotionEvent.obtain(1, eventTimeMs, action, x, y, 0);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
index cba9ee38..341b74b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
@@ -25,25 +25,27 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class PointerCountClassifierTest extends SysuiTestCase {
+public class PointerCountClassifierTest extends ClassifierTest {
- @Mock
- private FalsingDataProvider mDataProvider;
private FalsingClassifier mClassifier;
@Before
public void setup() {
- mClassifier = new PointerCountClassifier(mDataProvider);
+ super.setup();
+ mClassifier = new PointerCountClassifier(getDataProvider());
+ }
+
+ @After
+ public void tearDown() {
+ super.tearDown();
}
@Test
@@ -53,9 +55,7 @@
@Test
public void testPass_singlePointer() {
- MotionEvent motionEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
- mClassifier.onTouchEvent(motionEvent);
- motionEvent.recycle();
+ mClassifier.onTouchEvent(appendDownEvent(1, 1));
assertThat(mClassifier.isFalseTouch(), is(false));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
index 2ed7925..a6cabbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
@@ -31,8 +31,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,7 +44,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class ProximityClassifierTest extends SysuiTestCase {
+public class ProximityClassifierTest extends ClassifierTest {
private static final long NS_PER_MS = 1000000;
@@ -57,12 +56,18 @@
@Before
public void setup() {
+ super.setup();
MockitoAnnotations.initMocks(this);
when(mDataProvider.getInteractionType()).thenReturn(GENERIC);
when(mDistanceClassifier.isLongSwipe()).thenReturn(false);
mClassifier = new ProximityClassifier(mDistanceClassifier, mDataProvider);
}
+ @After
+ public void tearDown() {
+ super.tearDown();
+ }
+
@Test
public void testPass_uncovered() {
touchDown();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
index 4bb3c15..0355dc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
@@ -34,8 +34,6 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.SysuiTestCase;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,15 +43,15 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class TypeClassifierTest extends SysuiTestCase {
+public class TypeClassifierTest extends ClassifierTest {
@Mock
private FalsingDataProvider mDataProvider;
-
private FalsingClassifier mClassifier;
@Before
public void setup() {
+ super.setup();
MockitoAnnotations.initMocks(this);
mClassifier = new TypeClassifier(mDataProvider);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
index 9d09a7e..25a1a75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
@@ -18,128 +18,93 @@
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
-import com.android.systemui.SysuiTestCase;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Random;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class ZigZagClassifierTest extends SysuiTestCase {
+public class ZigZagClassifierTest extends ClassifierTest {
- private static final long NS_PER_MS = 1000000;
-
- @Mock
- private FalsingDataProvider mDataProvider;
private FalsingClassifier mClassifier;
- private List<MotionEvent> mMotionEvents = new ArrayList<>();
- private float mOffsetX = 0;
- private float mOffsetY = 0;
- private float mDx;
- private float mDy;
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
- when(mDataProvider.getXdpi()).thenReturn(100f);
- when(mDataProvider.getYdpi()).thenReturn(100f);
- when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents);
- mClassifier = new ZigZagClassifier(mDataProvider);
-
-
- // Calculate the response to these calls on the fly, otherwise Mockito gets bogged down
- // everytime we call appendMotionEvent.
- when(mDataProvider.getFirstRecentMotionEvent()).thenAnswer(
- (Answer<MotionEvent>) invocation -> mMotionEvents.get(0));
- when(mDataProvider.getLastMotionEvent()).thenAnswer(
- (Answer<MotionEvent>) invocation -> mMotionEvents.get(mMotionEvents.size() - 1));
- when(mDataProvider.isHorizontal()).thenAnswer(
- (Answer<Boolean>) invocation -> Math.abs(mDy) < Math.abs(mDx));
- when(mDataProvider.isVertical()).thenAnswer(
- (Answer<Boolean>) invocation -> Math.abs(mDy) > Math.abs(mDx));
- when(mDataProvider.isRight()).thenAnswer((Answer<Boolean>) invocation -> mDx > 0);
- when(mDataProvider.isUp()).thenAnswer((Answer<Boolean>) invocation -> mDy < 0);
+ super.setup();
+ mClassifier = new ZigZagClassifier(getDataProvider());
}
@After
public void tearDown() {
- clearMotionEvents();
+ super.tearDown();
}
@Test
public void testPass_fewTouchesVertical() {
assertThat(mClassifier.isFalseTouch(), is(false));
- appendMotionEvent(0, 0);
+ appendMoveEvent(0, 0);
assertThat(mClassifier.isFalseTouch(), is(false));
- appendMotionEvent(0, 100);
+ appendMoveEvent(0, 100);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
public void testPass_vertical() {
- appendMotionEvent(0, 0);
- appendMotionEvent(0, 100);
- appendMotionEvent(0, 200);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(0, 100);
+ appendMoveEvent(0, 200);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
public void testPass_fewTouchesHorizontal() {
assertThat(mClassifier.isFalseTouch(), is(false));
- appendMotionEvent(0, 0);
+ appendMoveEvent(0, 0);
assertThat(mClassifier.isFalseTouch(), is(false));
- appendMotionEvent(100, 0);
+ appendMoveEvent(100, 0);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
public void testPass_horizontal() {
- appendMotionEvent(0, 0);
- appendMotionEvent(100, 0);
- appendMotionEvent(200, 0);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(100, 0);
+ appendMoveEvent(200, 0);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
public void testFail_minimumTouchesVertical() {
- appendMotionEvent(0, 0);
- appendMotionEvent(0, 100);
- appendMotionEvent(0, 1);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(0, 100);
+ appendMoveEvent(0, 1);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void testFail_minimumTouchesHorizontal() {
- appendMotionEvent(0, 0);
- appendMotionEvent(100, 0);
- appendMotionEvent(1, 0);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(100, 0);
+ appendMoveEvent(1, 0);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void testPass_fortyFiveDegreesStraight() {
- appendMotionEvent(0, 0);
- appendMotionEvent(10, 10);
- appendMotionEvent(20, 20);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(10, 10);
+ appendMoveEvent(20, 20);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@@ -147,9 +112,9 @@
public void testPass_horizontalZigZagVerticalStraight() {
// This test looks just like testFail_horizontalZigZagVerticalStraight but with
// a longer y range, making it look straighter.
- appendMotionEvent(0, 0);
- appendMotionEvent(5, 100);
- appendMotionEvent(-5, 200);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(5, 100);
+ appendMoveEvent(-5, 200);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@@ -157,9 +122,9 @@
public void testPass_horizontalStraightVerticalZigZag() {
// This test looks just like testFail_horizontalStraightVerticalZigZag but with
// a longer x range, making it look straighter.
- appendMotionEvent(0, 0);
- appendMotionEvent(100, 5);
- appendMotionEvent(200, -5);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(100, 5);
+ appendMoveEvent(200, -5);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@@ -167,9 +132,9 @@
public void testFail_horizontalZigZagVerticalStraight() {
// This test looks just like testPass_horizontalZigZagVerticalStraight but with
// a shorter y range, making it look more crooked.
- appendMotionEvent(0, 0);
- appendMotionEvent(5, 10);
- appendMotionEvent(-5, 20);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(5, 10);
+ appendMoveEvent(-5, 20);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@@ -177,217 +142,217 @@
public void testFail_horizontalStraightVerticalZigZag() {
// This test looks just like testPass_horizontalStraightVerticalZigZag but with
// a shorter x range, making it look more crooked.
- appendMotionEvent(0, 0);
- appendMotionEvent(10, 5);
- appendMotionEvent(20, -5);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(10, 5);
+ appendMoveEvent(20, -5);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between0And45() {
- appendMotionEvent(0, 0);
- appendMotionEvent(100, 5);
- appendMotionEvent(200, 10);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(100, 5);
+ appendMoveEvent(200, 10);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(100, 0);
- appendMotionEvent(200, 10);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(100, 0);
+ appendMoveEvent(200, 10);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(100, -10);
- appendMotionEvent(200, 10);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(100, -10);
+ appendMoveEvent(200, 10);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(100, -10);
- appendMotionEvent(200, 50);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(100, -10);
+ appendMoveEvent(200, 50);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between45And90() {
- appendMotionEvent(0, 0);
- appendMotionEvent(10, 50);
- appendMotionEvent(8, 100);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(10, 50);
+ appendMoveEvent(8, 100);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(1, 800);
- appendMotionEvent(2, 900);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(1, 800);
+ appendMoveEvent(2, 900);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-10, 600);
- appendMotionEvent(30, 700);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-10, 600);
+ appendMoveEvent(30, 700);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(40, 100);
- appendMotionEvent(0, 101);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(40, 100);
+ appendMoveEvent(0, 101);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between90And135() {
- appendMotionEvent(0, 0);
- appendMotionEvent(-10, 50);
- appendMotionEvent(-24, 100);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-10, 50);
+ appendMoveEvent(-24, 100);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-20, 800);
- appendMotionEvent(-20, 900);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-20, 800);
+ appendMoveEvent(-20, 900);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(30, 600);
- appendMotionEvent(-10, 700);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(30, 600);
+ appendMoveEvent(-10, 700);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-80, 100);
- appendMotionEvent(-10, 101);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-80, 100);
+ appendMoveEvent(-10, 101);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between135And180() {
- appendMotionEvent(0, 0);
- appendMotionEvent(-120, 10);
- appendMotionEvent(-200, 20);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-120, 10);
+ appendMoveEvent(-200, 20);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-20, 8);
- appendMotionEvent(-40, 2);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-20, 8);
+ appendMoveEvent(-40, 2);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-500, -2);
- appendMotionEvent(-600, 70);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-500, -2);
+ appendMoveEvent(-600, 70);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-80, 100);
- appendMotionEvent(-100, 1);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-80, 100);
+ appendMoveEvent(-100, 1);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between180And225() {
- appendMotionEvent(0, 0);
- appendMotionEvent(-120, -10);
- appendMotionEvent(-200, -20);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-120, -10);
+ appendMoveEvent(-200, -20);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-20, -8);
- appendMotionEvent(-40, -2);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-20, -8);
+ appendMoveEvent(-40, -2);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-500, 2);
- appendMotionEvent(-600, -70);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-500, 2);
+ appendMoveEvent(-600, -70);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-80, -100);
- appendMotionEvent(-100, -1);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-80, -100);
+ appendMoveEvent(-100, -1);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between225And270() {
- appendMotionEvent(0, 0);
- appendMotionEvent(-12, -20);
- appendMotionEvent(-20, -40);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-12, -20);
+ appendMoveEvent(-20, -40);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-20, -130);
- appendMotionEvent(-40, -260);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-20, -130);
+ appendMoveEvent(-40, -260);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(1, -100);
- appendMotionEvent(-6, -200);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(1, -100);
+ appendMoveEvent(-6, -200);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-80, -100);
- appendMotionEvent(-10, -110);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-80, -100);
+ appendMoveEvent(-10, -110);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between270And315() {
- appendMotionEvent(0, 0);
- appendMotionEvent(12, -20);
- appendMotionEvent(20, -40);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(12, -20);
+ appendMoveEvent(20, -40);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(20, -130);
- appendMotionEvent(40, -260);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(20, -130);
+ appendMoveEvent(40, -260);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(-1, -100);
- appendMotionEvent(6, -200);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(-1, -100);
+ appendMoveEvent(6, -200);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(80, -100);
- appendMotionEvent(10, -110);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(80, -100);
+ appendMoveEvent(10, -110);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between315And360() {
- appendMotionEvent(0, 0);
- appendMotionEvent(120, -20);
- appendMotionEvent(200, -40);
+ appendMoveEvent(0, 0);
+ appendMoveEvent(120, -20);
+ appendMoveEvent(200, -40);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(200, -13);
- appendMotionEvent(400, -30);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(200, -13);
+ appendMoveEvent(400, -30);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(100, 10);
- appendMotionEvent(600, -20);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(100, 10);
+ appendMoveEvent(600, -20);
assertThat(mClassifier.isFalseTouch(), is(false));
- mMotionEvents.clear();
- appendMotionEvent(0, 0);
- appendMotionEvent(80, -100);
- appendMotionEvent(100, -1);
+ resetDataProvider();
+ appendMoveEvent(0, 0);
+ appendMoveEvent(80, -100);
+ appendMoveEvent(100, -1);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@@ -397,74 +362,50 @@
// We use a pre-determined seed to make this test repeatable.
Random rand = new Random(23);
for (int i = 0; i < 100; i++) {
- mOffsetX = rand.nextInt(2000) - 1000;
- mOffsetY = rand.nextInt(2000) - 1000;
+ setOffsetX(rand.nextInt(2000) - 1000);
+ setOffsetY(rand.nextInt(2000) - 1000);
try {
- clearMotionEvents();
+ resetDataProvider();
testPass_fewTouchesVertical();
- clearMotionEvents();
+ resetDataProvider();
testPass_vertical();
- clearMotionEvents();
+ resetDataProvider();
testFail_horizontalStraightVerticalZigZag();
- clearMotionEvents();
+ resetDataProvider();
testFail_horizontalZigZagVerticalStraight();
- clearMotionEvents();
+ resetDataProvider();
testFail_minimumTouchesHorizontal();
- clearMotionEvents();
+ resetDataProvider();
testFail_minimumTouchesVertical();
- clearMotionEvents();
+ resetDataProvider();
testPass_fewTouchesHorizontal();
- clearMotionEvents();
+ resetDataProvider();
testPass_fortyFiveDegreesStraight();
- clearMotionEvents();
+ resetDataProvider();
testPass_horizontal();
- clearMotionEvents();
+ resetDataProvider();
testPass_horizontalStraightVerticalZigZag();
- clearMotionEvents();
+ resetDataProvider();
testPass_horizontalZigZagVerticalStraight();
- clearMotionEvents();
+ resetDataProvider();
test_between0And45();
- clearMotionEvents();
+ resetDataProvider();
test_between45And90();
- clearMotionEvents();
+ resetDataProvider();
test_between90And135();
- clearMotionEvents();
+ resetDataProvider();
test_between135And180();
- clearMotionEvents();
+ resetDataProvider();
test_between180And225();
- clearMotionEvents();
+ resetDataProvider();
test_between225And270();
- clearMotionEvents();
+ resetDataProvider();
test_between270And315();
- clearMotionEvents();
+ resetDataProvider();
test_between315And360();
} catch (AssertionError e) {
throw new AssertionError("Random origin failure in iteration " + i, e);
}
}
}
-
- private void clearMotionEvents() {
- for (MotionEvent motionEvent : mMotionEvents) {
- motionEvent.recycle();
- }
- mMotionEvents.clear();
- }
-
- private void appendMotionEvent(float x, float y) {
- x += mOffsetX;
- y += mOffsetY;
-
- long eventTime = mMotionEvents.size() + 1;
- MotionEvent motionEvent = MotionEvent.obtain(1, eventTime, MotionEvent.ACTION_DOWN, x, y,
- 0);
- mMotionEvents.add(motionEvent);
-
- mDx = mDataProvider.getFirstRecentMotionEvent().getX()
- - mDataProvider.getLastMotionEvent().getX();
- mDy = mDataProvider.getFirstRecentMotionEvent().getY()
- - mDataProvider.getLastMotionEvent().getY();
-
- mClassifier.onTouchEvent(motionEvent);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index de8dcfe..7063ddf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationContentInflaterTest;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -78,7 +79,8 @@
mInstrumentation = InstrumentationRegistry.getInstrumentation();
StatusBarStateController stateController = mock(StatusBarStateController.class);
mGroupManager = new NotificationGroupManager(stateController);
- mHeadsUpManager = new HeadsUpManagerPhone(mContext, stateController);
+ mHeadsUpManager = new HeadsUpManagerPhone(mContext, stateController,
+ mock(KeyguardBypassController.class));
mHeadsUpManager.setUp(null, mGroupManager, null, null);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 7e6335d..524ad85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import org.junit.Assert;
import org.junit.Before;
@@ -57,11 +58,13 @@
private ExpandableNotificationRow mSecond;
@Mock
private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private KeyguardBypassController mBypassController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mRoundnessManager = new NotificationRoundnessManager();
+ mRoundnessManager = new NotificationRoundnessManager(mBypassController);
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
mFirst = testHelper.createRow();
@@ -260,15 +263,15 @@
}
@Test
- public void testTrackingHeadsUpNotRoundedIfPushingDown() {
+ public void testTrackingHeadsUpPartiallyRoundedIfPushingDown() {
mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.5f /* appearFraction */);
mRoundnessManager.setTrackingHeadsUp(mFirst);
mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
createSection(mSecond, mSecond),
createSection(null, null)
});
- Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
- Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ Assert.assertEquals(0.5f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(0.5f, mFirst.getCurrentTopRoundness(), 0.0f);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 66255dd..92173cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -200,17 +200,6 @@
}
@Test
- public void testAntiBurnInOffset() {
- final int burnInOffset = 30;
- mStackScroller.setAntiBurnInOffsetX(burnInOffset);
- mStackScroller.setHideAmount(0.0f, 0.0f);
- Assert.assertEquals(0 /* expected */, mStackScroller.getTranslationX(), 0.01 /* delta */);
- mStackScroller.setHideAmount(1.0f, 1.0f);
- Assert.assertEquals(burnInOffset /* expected */, mStackScroller.getTranslationX(),
- 0.01 /* delta */);
- }
-
- @Test
public void updateEmptyView_dndSuppressing() {
when(mEmptyShadeView.willBeGone()).thenReturn(true);
when(mBar.areNotificationsHidden()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 0479b4a..b45707e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -32,6 +32,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -56,6 +57,8 @@
private HeadsUpStatusBarView mHeadsUpStatusBarView;
private HeadsUpManagerPhone mHeadsUpManager;
private View mOperatorNameView;
+ private StatusBarStateController mStatusbarStateController;
+ private KeyguardBypassController mBypassController;
@Before
public void setUp() throws Exception {
@@ -67,16 +70,20 @@
mock(TextView.class));
mHeadsUpManager = mock(HeadsUpManagerPhone.class);
mOperatorNameView = new View(mContext);
+ mStatusbarStateController = mock(StatusBarStateController.class);
+ mBypassController = mock(KeyguardBypassController.class);
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
mHeadsUpManager,
+ mStatusbarStateController,
+ mBypassController,
mHeadsUpStatusBarView,
mStackScroller,
mPanelView,
new View(mContext),
mOperatorNameView,
new View(mContext));
- mHeadsUpAppearanceController.setExpandedHeight(0.0f, 0.0f);
+ mHeadsUpAppearanceController.setAppearFraction(0.0f, 0.0f);
}
@Test
@@ -139,11 +146,13 @@
@Test
public void testHeaderReadFromOldController() {
- mHeadsUpAppearanceController.setExpandedHeight(1.0f, 1.0f);
+ mHeadsUpAppearanceController.setAppearFraction(1.0f, 1.0f);
HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
mHeadsUpManager,
+ mStatusbarStateController,
+ mBypassController,
mHeadsUpStatusBarView,
mStackScroller,
mPanelView,
@@ -154,8 +163,8 @@
Assert.assertEquals(mHeadsUpAppearanceController.mExpandedHeight,
newController.mExpandedHeight, 0.0f);
- Assert.assertEquals(mHeadsUpAppearanceController.mExpandFraction,
- newController.mExpandFraction, 0.0f);
+ Assert.assertEquals(mHeadsUpAppearanceController.mAppearFraction,
+ newController.mAppearFraction, 0.0f);
Assert.assertEquals(mHeadsUpAppearanceController.mIsExpanded,
newController.mIsExpanded);
}
@@ -172,7 +181,7 @@
verify(mPanelView).removeVerticalTranslationListener(any());
verify(mPanelView).removeTrackingHeadsUpListener(any());
verify(mPanelView).setHeadsUpAppearanceController(any());
- verify(mStackScroller).removeOnExpandedHeightListener(any());
+ verify(mStackScroller).removeOnExpandedHeightChangedListener(any());
verify(mStackScroller).removeOnLayoutChangeListener(any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index a66345b..f8b9e68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -57,14 +57,16 @@
@Mock private VisualStabilityManager mVSManager;
@Mock private StatusBar mBar;
@Mock private StatusBarStateController mStatusBarStateController;
+ @Mock private KeyguardBypassController mBypassController;
private boolean mLivesPastNormalTime;
private final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
TestableHeadsUpManagerPhone(Context context, View statusBarWindowView,
NotificationGroupManager groupManager, StatusBar bar,
VisualStabilityManager vsManager,
- StatusBarStateController statusBarStateController) {
- super(context, statusBarStateController);
+ StatusBarStateController statusBarStateController,
+ KeyguardBypassController keyguardBypassController) {
+ super(context, statusBarStateController, keyguardBypassController);
setUp(statusBarWindowView, groupManager, bar, vsManager);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -84,7 +86,7 @@
.thenReturn(TEST_AUTO_DISMISS_TIME);
when(mVSManager.isReorderingAllowed()).thenReturn(true);
mHeadsUpManager = new TestableHeadsUpManagerPhone(mContext, mStatusBarWindowView,
- mGroupManager, mBar, mVSManager, mStatusBarStateController);
+ mGroupManager, mBar, mVSManager, mStatusBarStateController, mBypassController);
super.setUp();
mHeadsUpManager.mHandler = mTestHandler;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 4b5e1dd..d14b460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -120,7 +121,7 @@
new StatusBarStateControllerImpl(),
bypassController);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator,
- bypassController);
+ bypassController, mHeadsUpManager, mock(NotificationRoundnessManager.class));
mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
bypassController);
mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index 66eb299..3357be8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -42,7 +42,7 @@
@Before
public void setUp() {
mInner = WakeLock.createPartialInner(mContext, WakeLockTest.class.getName());
- mWakeLock = WakeLock.wrap(mInner);
+ mWakeLock = WakeLock.wrap(mInner, 20000);
}
@After
@@ -70,14 +70,6 @@
}
@Test
- public void wakeLock_refCounted() {
- mWakeLock.acquire(WHY);
- mWakeLock.acquire(WHY);
- mWakeLock.release(WHY);
- assertTrue(mInner.isHeld());
- }
-
- @Test
public void wakeLock_wrap() {
boolean[] ran = new boolean[1];
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index f8b7bcf..7920bbb 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -75,10 +75,6 @@
// pointers so they can be considered moving in the same direction.
private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
- // The minimal distance before we take the middle of the distance between
- // the two dragging pointers as opposed to use the location of the primary one.
- private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200;
-
// The timeout after which we are no longer trying to detect a gesture.
private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000;
@@ -115,10 +111,6 @@
// Helper to detect gestures.
private final AccessibilityGestureDetector mGestureDetector;
- // The scaled minimal distance before we take the middle of the distance between
- // the two dragging pointers as opposed to use the location of the primary one.
- private final int mScaledMinPointerDistanceToUseMiddleLocation;
-
// Helper class to track received pointers.
private final TouchState.ReceivedPointerTracker mReceivedPointerTracker;
@@ -188,9 +180,6 @@
} else {
mGestureDetector = detector;
}
- final float density = context.getResources().getDisplayMetrics().density;
- mScaledMinPointerDistanceToUseMiddleLocation =
- (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
}
@Override
@@ -634,11 +623,11 @@
} break;
case 2: {
if (isDraggingGesture(event)) {
+ // Adjust event location to the middle location of the two pointers.
final float firstPtrX = event.getX(0);
final float firstPtrY = event.getY(0);
final float secondPtrX = event.getX(1);
final float secondPtrY = event.getY(1);
-
final int pointerIndex = event.findPointerIndex(mDraggingPointerId);
final float deltaX =
(pointerIndex == 0) ? (secondPtrX - firstPtrX)
@@ -646,12 +635,7 @@
final float deltaY =
(pointerIndex == 0) ? (secondPtrY - firstPtrY)
: (firstPtrY - secondPtrY);
- final double distance = Math.hypot(deltaX, deltaY);
-
- if (distance > mScaledMinPointerDistanceToUseMiddleLocation) {
- event.offsetLocation(deltaX / 2, deltaY / 2);
- }
-
+ event.offsetLocation(deltaX / 2, deltaY / 2);
// If still dragging send a drag event.
sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits,
policyFlags);
@@ -1205,8 +1189,6 @@
+ ", mLongPressingPointerId: " + mLongPressingPointerId
+ ", mLongPressingPointerDeltaX: " + mLongPressingPointerDeltaX
+ ", mLongPressingPointerDeltaY: " + mLongPressingPointerDeltaY
- + ", mScaledMinPointerDistanceToUseMiddleLocation: "
- + mScaledMinPointerDistanceToUseMiddleLocation
+ ", mTempPoint: " + mTempPoint
+ " }";
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6e16e9d..f0321c1 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -193,7 +193,6 @@
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkStatsFactory;
import com.android.server.utils.PriorityDump;
import com.google.android.collect.Lists;
@@ -6801,8 +6800,8 @@
}
/**
- * Notify NetworkStatsService and NetworkStatsFactory that the set of active ifaces has changed,
- * or that one of the active iface's trackedproperties has changed.
+ * Notify NetworkStatsService that the set of active ifaces has changed, or that one of the
+ * active iface's tracked properties has changed.
*/
private void notifyIfacesChangedForNetworkStats() {
ensureRunningOnConnectivityServiceThread();
@@ -6812,16 +6811,12 @@
activeIface = activeLinkProperties.getInterfaceName();
}
- // CAUTION: Ordering matters between updateVpnInfos() and forceUpdateIfaces(), which
- // triggers a new poll. Trigger the poll first to ensure a snapshot is taken before
- // switching to the new state. This ensures that traffic does not get mis-attributed to
- // incorrect apps (including VPN app).
+ final VpnInfo[] vpnInfos = getAllVpnInfo();
try {
mStatsService.forceUpdateIfaces(
- getDefaultNetworks(), getAllNetworkState(), activeIface);
+ getDefaultNetworks(), getAllNetworkState(), activeIface, vpnInfos);
} catch (Exception ignored) {
}
- NetworkStatsFactory.updateVpnInfos(getAllVpnInfo());
}
@Override
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index e5fb506..59e0a28 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -34,7 +34,6 @@
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.STATS_PER_UID;
-import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.TrafficStats.UID_TETHERING;
@@ -91,7 +90,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.HexDump;
import com.android.internal.util.Preconditions;
-import com.android.server.net.NetworkStatsFactory;
import com.google.android.collect.Maps;
@@ -165,8 +163,6 @@
private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
new RemoteCallbackList<>();
- private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory();
-
@GuardedBy("mTetheringStatsProviders")
private final HashMap<ITetheringStatsProvider, String>
mTetheringStatsProviders = Maps.newHashMap();
@@ -1212,36 +1208,6 @@
}
@Override
- public NetworkStats getNetworkStatsSummaryDev() {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- return mStatsFactory.readNetworkStatsSummaryDev();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
- public NetworkStats getNetworkStatsSummaryXt() {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- return mStatsFactory.readNetworkStatsSummaryXt();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
- public NetworkStats getNetworkStatsDetail() {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- return mStatsFactory.readNetworkStatsDetail();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
public void setInterfaceQuota(String iface, long quotaBytes) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -1540,16 +1506,6 @@
return true;
}
- @Override
- public NetworkStats getNetworkStatsUidDetail(int uid, String[] ifaces) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- return mStatsFactory.readNetworkStatsDetail(uid, ifaces, TAG_ALL);
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
private class NetdTetheringStatsProvider extends ITetheringStatsProvider.Stub {
@Override
public NetworkStats getTetherStats(int how) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 1a6faec..e66e596 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1255,23 +1255,21 @@
}
}
- public void notifyPhysicalChannelConfiguration(List<PhysicalChannelConfig> configs) {
- notifyPhysicalChannelConfigurationForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
- configs);
- }
-
- public void notifyPhysicalChannelConfigurationForSubscriber(int subId,
+ /**
+ * Notify physical channel configuration according to subscripton ID and phone ID
+ */
+ public void notifyPhysicalChannelConfigurationForSubscriber(int phoneId, int subId,
List<PhysicalChannelConfig> configs) {
if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) {
return;
}
if (VDBG) {
- log("notifyPhysicalChannelConfiguration: subId=" + subId + " configs=" + configs);
+ log("notifyPhysicalChannelConfiguration: subId=" + subId + " phoneId=" + phoneId
+ + " configs=" + configs);
}
synchronized (mRecords) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
mPhysicalChannelConfigs.set(phoneId, configs);
for (Record r : mRecords) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 7ab70fa..ed64475 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -42,7 +42,7 @@
static final boolean DEBUG_ALL = false;
// Available log categories in the activity manager package.
- static final boolean DEBUG_ANR = true; // STOPSHIP disable it (b/113252928)
+ static final boolean DEBUG_ANR = false;
static final boolean DEBUG_BACKGROUND_CHECK = DEBUG_ALL || false;
static final boolean DEBUG_BACKUP = DEBUG_ALL || false;
static final boolean DEBUG_BROADCAST = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e6379f8..f0243c3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3767,9 +3767,7 @@
ArrayList<Integer> nativePids) {
ArrayList<Integer> extraPids = null;
- if (DEBUG_ANR) {
- Slog.d(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);
- }
+ Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);
// Measure CPU usage as soon as we're called in order to get a realistic sampling
// of the top users at the time of the request.
@@ -3791,8 +3789,8 @@
if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
extraPids.add(stats.pid);
- } else if (DEBUG_ANR) {
- Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: "
+ } else {
+ Slog.i(TAG, "Skipping next CPU consuming process, not a java proc: "
+ stats.pid);
}
}
@@ -3810,9 +3808,6 @@
if (tracesFile == null) {
return null;
}
- if (DEBUG_ANR) {
- Slog.d(TAG, "Dumping to " + tracesFile.getAbsolutePath());
- }
dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
return tracesFile;
@@ -3905,6 +3900,8 @@
public static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
+ Slog.i(TAG, "Dumping to " + tracesFile);
+
// We don't need any sort of inotify based monitoring when we're dumping traces via
// tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
// control of all writes to the file in question.
@@ -3916,7 +3913,7 @@
if (firstPids != null) {
int num = firstPids.size();
for (int i = 0; i < num; i++) {
- if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid " + firstPids.get(i));
+ Slog.i(TAG, "Collecting stacks for pid " + firstPids.get(i));
final long timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile,
remainingTime);
@@ -3936,7 +3933,7 @@
// Next collect the stacks of the native pids
if (nativePids != null) {
for (int pid : nativePids) {
- if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
+ Slog.i(TAG, "Collecting stacks for native pid " + pid);
final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
final long start = SystemClock.elapsedRealtime();
@@ -3960,7 +3957,7 @@
// Lastly, dump stacks for all extra PIDs from the CPU tracker.
if (extraPids != null) {
for (int pid : extraPids) {
- if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + pid);
+ Slog.i(TAG, "Collecting stacks for extra pid " + pid);
final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
@@ -3976,6 +3973,7 @@
}
}
}
+ Slog.i(TAG, "Done dumping");
}
@Override
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index e2579a9..a2613d8 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -434,7 +434,7 @@
for (int i = 0; i < N; i++) {
ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
- if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now,
+ if (computeOomAdjLocked(app, app.getCurRawAdj(), TOP_APP, true, now,
true)) {
retryCycles = true;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 884ecba..4d09c3b7 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -40,9 +40,11 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
+import android.util.PrintWriterPrinter;
import com.android.internal.annotations.GuardedBy;
+import java.io.PrintWriter;
/** @hide */
/*package*/ final class AudioDeviceBroker {
@@ -597,6 +599,15 @@
}
}
+ /*package*/ void dump(PrintWriter pw, String prefix) {
+ if (mBrokerHandler != null) {
+ pw.println(prefix + "Message handler (watch for unhandled messages):");
+ mBrokerHandler.dump(new PrintWriterPrinter(pw), prefix + " ");
+ } else {
+ pw.println("Message handler is null");
+ }
+ }
+
//---------------------------------------------------------------------
// Internal handling of messages
// These methods are ALL synchronous, in response to message handling in BrokerHandler
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 972f30b..d3cafb7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6421,11 +6421,12 @@
dumpAudioPolicies(pw);
mDynPolicyLogger.dump(pw);
-
mPlaybackMonitor.dump(pw);
-
mRecordMonitor.dump(pw);
+ pw.println("\nAudioDeviceBroker:");
+ mDeviceBroker.dump(pw, " ");
+
pw.println("\n");
pw.println("\nEvent logs:");
mModeLogger.dump(pw);
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 90edcb5..def7f75 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -54,7 +54,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -503,8 +502,6 @@
try {
if (args.length > 1 && "--hal".equals(args[0])) {
dumpHal(fd, Arrays.copyOfRange(args, 1, args.length, args.getClass()));
- } else if (args.length > 0 && "--proto".equals(args[0])) {
- dumpProto(fd);
} else {
dumpInternal(pw);
}
@@ -1296,49 +1293,6 @@
mUsageStats.print(pw);
}
- private void dumpProto(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
- for (UserInfo user : UserManager.get(getContext()).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
-
- final long userToken = proto.start(FaceServiceDumpProto.USERS);
-
- proto.write(FaceUserStatsProto.USER_ID, userId);
- proto.write(FaceUserStatsProto.NUM_FACES,
- getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
-
- // Normal face authentications (e.g. lockscreen)
- final PerformanceStats normal = mPerformanceMap.get(userId);
- if (normal != null) {
- final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
- proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
- proto.write(FaceActionStatsProto.REJECT, normal.reject);
- proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
- proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
- proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
- proto.end(countsToken);
- }
-
- // Statistics about secure face transactions (e.g. to unlock password
- // storage, make secure purchases, etc.)
- final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
- if (crypto != null) {
- final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
- proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
- proto.write(FaceActionStatsProto.REJECT, crypto.reject);
- proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
- proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
- proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
- proto.end(countsToken);
- }
-
- proto.end(userToken);
- }
- proto.flush();
- mPerformanceMap.clear();
- mCryptoPerformanceMap.clear();
- }
-
private void dumpHal(FileDescriptor fd, String[] args) {
// WARNING: CDD restricts image data from leaving TEE unencrypted on
// production devices:
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 395319d..dd3876d 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -64,6 +64,7 @@
private DisplayWhiteBalanceThrottler mThrottler;
private final float mLowLightAmbientColorTemperature;
+ private final float mHighLightAmbientColorTemperature;
private float mAmbientColorTemperature;
@@ -83,9 +84,17 @@
// A piecewise linear relationship between ambient and display color temperatures.
private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
+ // In very low or very high brightness conditions ambient EQ should to set to a default
+ // instead of using mAmbientToDisplayColorTemperatureSpline. However, setting ambient EQ
+ // based on thresholds can cause the display to rapidly change color temperature. To solve
+ // this, mLowLightAmbientBrightnessToBiasSpline and mHighLightAmbientBrightnessToBiasSpline
+ // are used to smoothly interpolate from ambient color temperature to the defaults.
// A piecewise linear relationship between low light brightness and low light bias.
private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSpline;
+ // A piecewise linear relationship between high light brightness and high light bias.
+ private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSpline;
+
/**
* @param brightnessSensor
* The sensor used to detect changes in the ambient brightness.
@@ -100,12 +109,22 @@
* @param throttler
* The throttler used to determine whether the new display color temperature should be
* updated or not.
- * @param lowLightAmbientBrightnessThreshold
- * The ambient brightness threshold beneath which we fall back to a fixed ambient color
- * temperature.
+ * @param lowLightAmbientBrightnesses
+ * The ambient brightness used to map the ambient brightnesses to the biases used to
+ * interpolate to lowLightAmbientColorTemperature.
+ * @param lowLightAmbientBiases
+ * The biases used to map the ambient brightnesses to the biases used to interpolate to
+ * lowLightAmbientColorTemperature.
* @param lowLightAmbientColorTemperature
- * The ambient color temperature to which we fall back when the ambient brightness drops
- * beneath a certain threshold.
+ * The ambient color temperature to which we interpolate to based on the low light curve.
+ * @param highLightAmbientBrightnesses
+ * The ambient brightness used to map the ambient brightnesses to the biases used to
+ * interpolate to highLightAmbientColorTemperature.
+ * @param highLightAmbientBiases
+ * The biases used to map the ambient brightnesses to the biases used to interpolate to
+ * highLightAmbientColorTemperature.
+ * @param highLightAmbientColorTemperature
+ * The ambient color temperature to which we interpolate to based on the high light curve.
* @param ambientColorTemperatures
* The ambient color tempeartures used to map the ambient color temperature to the display
* color temperature (or null if no mapping is necessary).
@@ -128,6 +147,8 @@
@NonNull DisplayWhiteBalanceThrottler throttler,
float[] lowLightAmbientBrightnesses, float[] lowLightAmbientBiases,
float lowLightAmbientColorTemperature,
+ float[] highLightAmbientBrightnesses, float[] highLightAmbientBiases,
+ float highLightAmbientColorTemperature,
float[] ambientColorTemperatures, float[] displayColorTemperatures) {
validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
colorTemperatureFilter, throttler);
@@ -140,6 +161,7 @@
mColorTemperatureFilter = colorTemperatureFilter;
mThrottler = throttler;
mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature;
+ mHighLightAmbientColorTemperature = highLightAmbientColorTemperature;
mAmbientColorTemperature = -1.0f;
mPendingAmbientColorTemperature = -1.0f;
mLastAmbientColorTemperature = -1.0f;
@@ -158,12 +180,40 @@
mLowLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY)
!= 1.0f) {
Slog.d(TAG, "invalid low light ambient brightness to bias spline, "
- + "bias must begin at 0.0 and end at 1.0");
+ + "bias must begin at 0.0 and end at 1.0.");
mLowLightAmbientBrightnessToBiasSpline = null;
}
}
try {
+ mHighLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline(
+ highLightAmbientBrightnesses, highLightAmbientBiases);
+ } catch (Exception e) {
+ Slog.e(TAG, "failed to create high light ambient brightness to bias spline.", e);
+ mHighLightAmbientBrightnessToBiasSpline = null;
+ }
+ if (mHighLightAmbientBrightnessToBiasSpline != null) {
+ if (mHighLightAmbientBrightnessToBiasSpline.interpolate(0.0f) != 0.0f ||
+ mHighLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY)
+ != 1.0f) {
+ Slog.d(TAG, "invalid high light ambient brightness to bias spline, "
+ + "bias must begin at 0.0 and end at 1.0.");
+ mHighLightAmbientBrightnessToBiasSpline = null;
+ }
+ }
+
+ if (mLowLightAmbientBrightnessToBiasSpline != null &&
+ mHighLightAmbientBrightnessToBiasSpline != null) {
+ if (lowLightAmbientBrightnesses[lowLightAmbientBrightnesses.length - 1] >
+ highLightAmbientBrightnesses[0]) {
+ Slog.d(TAG, "invalid low light and high light ambient brightness to bias spline "
+ + "combination, defined domains must not intersect.");
+ mLowLightAmbientBrightnessToBiasSpline = null;
+ mHighLightAmbientBrightnessToBiasSpline = null;
+ }
+ }
+
+ try {
mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
ambientColorTemperatures, displayColorTemperatures);
} catch (Exception e) {
@@ -264,6 +314,7 @@
mColorTemperatureFilter.dump(writer);
mThrottler.dump(writer);
writer.println(" mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
+ writer.println(" mHighLightAmbientColorTemperature=" + mHighLightAmbientColorTemperature);
writer.println(" mAmbientColorTemperature=" + mAmbientColorTemperature);
writer.println(" mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
@@ -273,6 +324,8 @@
+ mAmbientToDisplayColorTemperatureSpline);
writer.println(" mLowLightAmbientBrightnessToBiasSpline="
+ mLowLightAmbientBrightnessToBiasSpline);
+ writer.println(" mHighLightAmbientBrightnessToBiasSpline="
+ + mHighLightAmbientBrightnessToBiasSpline);
}
@Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
@@ -309,6 +362,12 @@
bias * ambientColorTemperature + (1.0f - bias)
* mLowLightAmbientColorTemperature;
}
+ if (mHighLightAmbientBrightnessToBiasSpline != null) {
+ float bias = mHighLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
+ ambientColorTemperature =
+ (1.0f - bias) * ambientColorTemperature + bias
+ * mHighLightAmbientColorTemperature;
+ }
if (mAmbientColorTemperatureOverride != -1.0f) {
if (mLoggingEnabled) {
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index b1b465e..4df7d6b 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -72,6 +72,15 @@
final float lowLightAmbientColorTemperature = getFloat(resources,
com.android.internal.R.dimen
.config_displayWhiteBalanceLowLightAmbientColorTemperature);
+ final float[] displayWhiteBalanceHighLightAmbientBrightnesses = getFloatArray(resources,
+ com.android.internal.R.array
+ .config_displayWhiteBalanceHighLightAmbientBrightnesses);
+ final float[] displayWhiteBalanceHighLightAmbientBiases = getFloatArray(resources,
+ com.android.internal.R.array
+ .config_displayWhiteBalanceHighLightAmbientBiases);
+ final float highLightAmbientColorTemperature = getFloat(resources,
+ com.android.internal.R.dimen
+ .config_displayWhiteBalanceHighLightAmbientColorTemperature);
final float[] ambientColorTemperatures = getFloatArray(resources,
com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
final float[] displayColorTempeartures = getFloatArray(resources,
@@ -80,6 +89,8 @@
brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
throttler, displayWhiteBalanceLowLightAmbientBrightnesses,
displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature,
+ displayWhiteBalanceHighLightAmbientBrightnesses,
+ displayWhiteBalanceHighLightAmbientBiases, highLightAmbientColorTemperature,
ambientColorTemperatures, displayColorTempeartures);
brightnessSensor.setCallbacks(controller);
colorTemperatureSensor.setCallbacks(controller);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index adc1561..5bd4b20 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -54,6 +54,7 @@
import android.media.session.ISession2TokensListener;
import android.media.session.ISessionCallback;
import android.media.session.ISessionManager;
+import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.net.Uri;
@@ -1183,6 +1184,9 @@
}
/**
+ * Dispaches media key events. This is called when the foreground activity didn't handled
+ * the incoming media key event.
+ * <p>
* Handles the dispatching of the media button events to one of the
* registered listeners, or if there was none, broadcast an
* ACTION_MEDIA_BUTTON intent to the rest of the system.
@@ -1262,6 +1266,18 @@
}
}
+ /**
+ * Dispatches media key events to session as system service. This is used only when the
+ * foreground activity has set
+ * {@link android.app.Activity#setMediaController(MediaController)} and a media key was
+ * pressed.
+ *
+ * @param packageName The caller's package name, obtained by Context#getPackageName()
+ * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName()
+ * @param sessionToken token for the session that the controller is pointing to
+ * @param keyEvent media key event
+ * @see #dispatchVolumeKeyEvent
+ */
@Override
public boolean dispatchMediaKeyEventToSessionAsSystemService(String packageName,
MediaSession.Token sessionToken, KeyEvent keyEvent) {
@@ -1272,9 +1288,7 @@
synchronized (mLock) {
MediaSessionRecord record = getMediaSessionRecordLocked(sessionToken);
if (record == null) {
- if (DEBUG) {
- Log.d(TAG, "Failed to find session to dispatch key event.");
- }
+ Log.w(TAG, "Failed to find session to dispatch key event.");
return false;
}
if (DEBUG) {
@@ -1452,9 +1466,12 @@
}
/**
+ * Dispaches volume key events. This is called when the foreground activity didn't handled
+ * the incoming volume key event.
+ * <p>
* Handles the dispatching of the volume button events to one of the
* registered listeners. If there's a volume key long-press listener and
- * there's no active global priority session, long-pressess will be sent to the
+ * there's no active global priority session, long-presses will be sent to the
* long-press listener instead of adjusting volume.
*
* @param packageName The caller's package name, obtained by Context#getPackageName()
@@ -1471,6 +1488,7 @@
* or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
* @param stream stream type to adjust volume.
* @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
+ * @see #dispatchVolumeKeyEventToSessionAsSystemService
*/
@Override
public void dispatchVolumeKeyEvent(String packageName, String opPackageName,
@@ -1597,6 +1615,18 @@
}
}
+ /**
+ * Dispatches volume key events to session as system service. This is used only when the
+ * foreground activity has set
+ * {@link android.app.Activity#setMediaController(MediaController)} and a hardware volume
+ * key was pressed.
+ *
+ * @param packageName The caller's package name, obtained by Context#getPackageName()
+ * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName()
+ * @param sessionToken token for the session that the controller is pointing to
+ * @param keyEvent volume key event
+ * @see #dispatchVolumeKeyEvent
+ */
@Override
public void dispatchVolumeKeyEventToSessionAsSystemService(String packageName,
String opPackageName, MediaSession.Token sessionToken, KeyEvent keyEvent) {
@@ -1607,9 +1637,10 @@
synchronized (mLock) {
MediaSessionRecord record = getMediaSessionRecordLocked(sessionToken);
if (record == null) {
- if (DEBUG) {
- Log.d(TAG, "Failed to find session to dispatch key event.");
- }
+ Log.w(TAG, "Failed to find session to dispatch key event, token="
+ + sessionToken + ". Fallbacks to the default handling.");
+ dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, true,
+ keyEvent, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
return;
}
if (DEBUG) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 7687718..3ca1803 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -78,17 +78,17 @@
* <p>In order to prevent deadlocks, critical sections protected by this lock SHALL NOT call out
* to other code that will acquire other locks within the system server. See b/134244752.
*/
- private static final Object sPersistentDataLock = new Object();
+ private final Object mPersistentDataLock = new Object();
/** Set containing info about active VPNs and their underlying networks. */
- private static volatile VpnInfo[] sVpnInfos = new VpnInfo[0];
+ private volatile VpnInfo[] mVpnInfos = new VpnInfo[0];
// A persistent snapshot of cumulative stats since device start
- @GuardedBy("sPersistentDataLock")
+ @GuardedBy("mPersistentDataLock")
private NetworkStats mPersistSnapshot;
// The persistent snapshot of tun and 464xlat adjusted stats since device start
- @GuardedBy("sPersistentDataLock")
+ @GuardedBy("mPersistentDataLock")
private NetworkStats mTunAnd464xlatAdjustedStats;
/**
@@ -97,12 +97,13 @@
* Because counters must never roll backwards, once a given interface is stacked on top of an
* underlying interface, the stacked interface can never be stacked on top of
* another interface. */
- private static final ConcurrentHashMap<String, String> sStackedIfaces
+ private final ConcurrentHashMap<String, String> mStackedIfaces
= new ConcurrentHashMap<>();
- public static void noteStackedIface(String stackedIface, String baseIface) {
+ /** Informs the factory of a new stacked interface. */
+ public void noteStackedIface(String stackedIface, String baseIface) {
if (stackedIface != null && baseIface != null) {
- sStackedIfaces.put(stackedIface, baseIface);
+ mStackedIfaces.put(stackedIface, baseIface);
}
}
@@ -115,13 +116,8 @@
*
* @param vpnArray The snapshot of the currently-running VPNs.
*/
- public static void updateVpnInfos(VpnInfo[] vpnArray) {
- sVpnInfos = vpnArray.clone();
- }
-
- @VisibleForTesting
- public static VpnInfo[] getVpnInfos() {
- return sVpnInfos.clone();
+ public void updateVpnInfos(VpnInfo[] vpnArray) {
+ mVpnInfos = vpnArray.clone();
}
/**
@@ -132,7 +128,7 @@
* {@link #noteStackedIface(String, String)}, but only interfaces noted before this method
* is called are guaranteed to be included.
*/
- public static String[] augmentWithStackedInterfaces(@Nullable String[] requiredIfaces) {
+ public String[] augmentWithStackedInterfaces(@Nullable String[] requiredIfaces) {
if (requiredIfaces == NetworkStats.INTERFACES_ALL) {
return null;
}
@@ -142,7 +138,7 @@
// elements as they existed upon construction exactly once, and may
// (but are not guaranteed to) reflect any modifications subsequent to construction".
// This is enough here.
- for (Map.Entry<String, String> entry : sStackedIfaces.entrySet()) {
+ for (Map.Entry<String, String> entry : mStackedIfaces.entrySet()) {
if (relatedIfaces.contains(entry.getKey())) {
relatedIfaces.add(entry.getValue());
} else if (relatedIfaces.contains(entry.getValue())) {
@@ -158,17 +154,12 @@
* Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
* @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map, boolean)
*/
- public static void apply464xlatAdjustments(NetworkStats baseTraffic,
+ public void apply464xlatAdjustments(NetworkStats baseTraffic,
NetworkStats stackedTraffic, boolean useBpfStats) {
- NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces,
+ NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces,
useBpfStats);
}
- @VisibleForTesting
- public static void clearStackedIfaces() {
- sStackedIfaces.clear();
- }
-
public NetworkStatsFactory() {
this(new File("/proc/"), new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists());
}
@@ -179,8 +170,10 @@
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
mUseBpfStats = useBpfStats;
- mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
- mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
+ synchronized (mPersistentDataLock) {
+ mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
+ mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
+ }
}
public NetworkStats readBpfNetworkStatsDev() throws IOException {
@@ -302,7 +295,7 @@
return readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
}
- @GuardedBy("sPersistentDataLock")
+ @GuardedBy("mPersistentDataLock")
private void requestSwapActiveStatsMapLocked() throws RemoteException {
// Ask netd to do a active map stats swap. When the binder call successfully returns,
// the system server should be able to safely read and clean the inactive map
@@ -326,9 +319,9 @@
int limitUid, String[] limitIfaces, int limitTag) throws IOException {
// In order to prevent deadlocks, anything protected by this lock MUST NOT call out to other
// code that will acquire other locks within the system server. See b/134244752.
- synchronized (sPersistentDataLock) {
+ synchronized (mPersistentDataLock) {
// Take a reference. If this gets swapped out, we still have the old reference.
- final VpnInfo[] vpnArray = sVpnInfos;
+ final VpnInfo[] vpnArray = mVpnInfos;
// Take a defensive copy. mPersistSnapshot is mutated in some cases below
final NetworkStats prev = mPersistSnapshot.clone();
@@ -377,7 +370,7 @@
}
}
- @GuardedBy("sPersistentDataLock")
+ @GuardedBy("mPersistentDataLock")
private NetworkStats adjustForTunAnd464Xlat(
NetworkStats uidDetailStats, NetworkStats previousStats, VpnInfo[] vpnArray) {
// Calculate delta from last snapshot
@@ -387,7 +380,7 @@
// network, the overhead is their fault.
// No locking here: apply464xlatAdjustments behaves fine with an add-only
// ConcurrentHashMap.
- delta.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats);
+ delta.apply464xlatAdjustments(mStackedIfaces, mUseBpfStats);
// Migrate data usage over a VPN to the TUN network.
for (VpnInfo info : vpnArray) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 6dae8ba..7991c0a 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -131,6 +131,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FileRotator;
@@ -181,6 +182,7 @@
private final Context mContext;
private final INetworkManagementService mNetworkManager;
+ private final NetworkStatsFactory mStatsFactory;
private final AlarmManager mAlarmManager;
private final Clock mClock;
private final TelephonyManager mTeleManager;
@@ -336,8 +338,8 @@
NetworkStatsService service = new NetworkStatsService(context, networkManager, alarmManager,
wakeLock, getDefaultClock(), TelephonyManager.getDefault(),
- new DefaultNetworkStatsSettings(context), new NetworkStatsObservers(),
- getDefaultSystemDir(), getDefaultBaseDir());
+ new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(),
+ new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir());
service.registerLocalService();
HandlerThread handlerThread = new HandlerThread(TAG);
@@ -354,7 +356,8 @@
NetworkStatsService(Context context, INetworkManagementService networkManager,
AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock,
TelephonyManager teleManager, NetworkStatsSettings settings,
- NetworkStatsObservers statsObservers, File systemDir, File baseDir) {
+ NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir,
+ File baseDir) {
mContext = checkNotNull(context, "missing Context");
mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService");
mAlarmManager = checkNotNull(alarmManager, "missing AlarmManager");
@@ -362,6 +365,7 @@
mSettings = checkNotNull(settings, "missing NetworkStatsSettings");
mTeleManager = checkNotNull(teleManager, "missing TelephonyManager");
mWakeLock = checkNotNull(wakeLock, "missing WakeLock");
+ mStatsFactory = checkNotNull(factory, "missing factory");
mStatsObservers = checkNotNull(statsObservers, "missing NetworkStatsObservers");
mSystemDir = checkNotNull(systemDir, "missing systemDir");
mBaseDir = checkNotNull(baseDir, "missing baseDir");
@@ -759,14 +763,7 @@
// TODO: switch to data layer stats once kernel exports
// for now, read network layer stats and flatten across all ifaces
- final long token = Binder.clearCallingIdentity();
- final NetworkStats networkLayer;
- try {
- networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid,
- NetworkStats.INTERFACES_ALL);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ final NetworkStats networkLayer = readNetworkStatsUidDetail(uid, INTERFACES_ALL, TAG_ALL);
// splice in operation counts
networkLayer.spliceOperationsFrom(mUidOperations);
@@ -788,7 +785,7 @@
public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
try {
final String[] ifacesToQuery =
- NetworkStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
+ mStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
return getNetworkStatsUidDetail(ifacesToQuery);
} catch (RemoteException e) {
Log.wtf(TAG, "Error compiling UID stats", e);
@@ -840,7 +837,8 @@
public void forceUpdateIfaces(
Network[] defaultNetworks,
NetworkState[] networkStates,
- String activeIface) {
+ String activeIface,
+ VpnInfo[] vpnInfos) {
checkNetworkStackPermission(mContext);
final long token = Binder.clearCallingIdentity();
@@ -849,6 +847,11 @@
} finally {
Binder.restoreCallingIdentity(token);
}
+
+ // Update the VPN underlying interfaces only after the poll is made and tun data has been
+ // migrated. Otherwise the migration would use the new interfaces instead of the ones that
+ // were current when the polled data was transferred.
+ mStatsFactory.updateVpnInfos(vpnInfos);
}
@Override
@@ -1197,7 +1200,7 @@
mobileIfaces.add(stackedIface);
}
- NetworkStatsFactory.noteStackedIface(stackedIface, baseIface);
+ mStatsFactory.noteStackedIface(stackedIface, baseIface);
}
}
}
@@ -1227,7 +1230,7 @@
final NetworkStats xtSnapshot = getNetworkStatsXt();
Trace.traceEnd(TRACE_TAG_NETWORK);
Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotDev");
- final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
+ final NetworkStats devSnapshot = readNetworkStatsSummaryDev();
Trace.traceEnd(TRACE_TAG_NETWORK);
// Tethering snapshot for dev and xt stats. Counts per-interface data from tethering stats
@@ -1621,6 +1624,30 @@
}
}
+ private NetworkStats readNetworkStatsSummaryDev() {
+ try {
+ return mStatsFactory.readNetworkStatsSummaryDev();
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private NetworkStats readNetworkStatsSummaryXt() {
+ try {
+ return mStatsFactory.readNetworkStatsSummaryXt();
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private NetworkStats readNetworkStatsUidDetail(int uid, String[] ifaces, int tag) {
+ try {
+ return mStatsFactory.readNetworkStatsDetail(uid, ifaces, tag);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
/**
* Return snapshot of current UID statistics, including any
* {@link TrafficStats#UID_TETHERING}, video calling data usage, and {@link #mUidOperations}
@@ -1631,13 +1658,12 @@
*/
private NetworkStats getNetworkStatsUidDetail(String[] ifaces)
throws RemoteException {
- final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL,
- ifaces);
+ final NetworkStats uidSnapshot = readNetworkStatsUidDetail(UID_ALL, ifaces, TAG_ALL);
// fold tethering stats and operations into uid snapshot
final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
- NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot,
+ mStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot,
mUseBpfTrafficStats);
uidSnapshot.combineAllValues(tetherSnapshot);
@@ -1648,7 +1674,7 @@
final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
if (vtStats != null) {
vtStats.filter(UID_ALL, ifaces, TAG_ALL);
- NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats,
+ mStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats,
mUseBpfTrafficStats);
uidSnapshot.combineAllValues(vtStats);
}
@@ -1662,7 +1688,7 @@
* Return snapshot of current XT statistics with video calling data usage statistics.
*/
private NetworkStats getNetworkStatsXt() throws RemoteException {
- final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
+ final NetworkStats xtSnapshot = readNetworkStatsSummaryXt();
final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 6594751..4f7c8c8 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -47,12 +47,10 @@
import android.content.pm.dex.DexMetadataHelper;
import android.os.FileUtils;
import android.os.PowerManager;
-import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.os.storage.StorageManager;
import android.util.Log;
import android.util.Slog;
@@ -269,10 +267,7 @@
return DEX_OPT_SKIPPED;
}
- // TODO(calin): there's no need to try to create the oat dir over and over again,
- // especially since it involve an extra installd call. We should create
- // if (if supported) on the fly during the dexopt call.
- String oatDir = createOatDirIfSupported(pkg, isa);
+ String oatDir = getPackageOatDirIfSupported(pkg);
Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
+ " pkg=" + pkg.applicationInfo.packageName + " isa=" + isa
@@ -638,7 +633,7 @@
}
/**
- * Creates oat dir for the specified package if needed and supported.
+ * Gets oat dir for the specified package if needed and supported.
* In certain cases oat directory
* <strong>cannot</strong> be created:
* <ul>
@@ -646,29 +641,19 @@
* <li>Package location is not a directory, i.e. monolithic install.</li>
* </ul>
*
- * @return Absolute path to the oat directory or null, if oat directory
- * cannot be created.
+ * @return Absolute path to the oat directory or null, if oat directories
+ * not needed or unsupported for the package.
*/
@Nullable
- private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet) {
+ private String getPackageOatDirIfSupported(PackageParser.Package pkg) {
if (!pkg.canHaveOatDir()) {
return null;
}
File codePath = new File(pkg.codePath);
- if (codePath.isDirectory()) {
- // TODO(calin): why do we create this only if the codePath is a directory? (i.e for
- // cluster packages). It seems that the logic for the folder creation is
- // split between installd and here.
- File oatDir = getOatDir(codePath);
- try {
- mInstaller.createOatDir(oatDir.getAbsolutePath(), dexInstructionSet);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to create oat dir", e);
- return null;
- }
- return oatDir.getAbsolutePath();
+ if (!codePath.isDirectory()) {
+ return null;
}
- return null;
+ return getOatDir(codePath).getAbsolutePath();
}
static File getOatDir(File codePath) {
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 3a78aa2..037293f 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -50,6 +50,7 @@
import android.provider.Telephony;
import android.telecom.TelecomManager;
import android.util.ArraySet;
+import android.util.LongSparseLongArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -58,6 +59,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.IntPair;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -147,11 +149,9 @@
PermissionInfo perm = dangerousPerms.get(i);
if (perm.isHardRestricted() || perm.backgroundPermission != null) {
- appOpsService.startWatchingMode(AppOpsManager.permissionToOpCode(perm.name),
- null, appOpsListener);
+ appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
} else if (perm.isSoftRestricted()) {
- appOpsService.startWatchingMode(AppOpsManager.permissionToOpCode(perm.name),
- null, appOpsListener);
+ appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
SoftRestrictedPermissionPolicy policy =
SoftRestrictedPermissionPolicy.forPermission(null, null, null,
@@ -167,6 +167,25 @@
}
}
+ /**
+ * Get op that controls the access related to the permission.
+ *
+ * <p>Usually the permission-op relationship is 1:1 but some permissions (e.g. fine location)
+ * {@link AppOpsManager#sOpToSwitch share an op} to control the access.
+ *
+ * @param permission The permission
+ *
+ * @return The op that controls the access of the permission
+ */
+ private static int getSwitchOp(@NonNull String permission) {
+ int op = AppOpsManager.permissionToOpCode(permission);
+ if (op == OP_NONE) {
+ return OP_NONE;
+ }
+
+ return AppOpsManager.opToSwitch(op);
+ }
+
private void synchronizePackagePermissionsAndAppOpsAsyncForUser(@NonNull String packageName,
@UserIdInt int changedUserId) {
if (isStarted(changedUserId)) {
@@ -430,40 +449,89 @@
* <p>This processes ops previously added by {@link #addOpIfRestricted}
*/
private void syncPackages() {
+ // Remember which ops were already set. This makes sure that we always set the most
+ // permissive mode if two OpChanges are scheduled. This can e.g. happen if two
+ // permissions change the same op. See {@link #getSwitchOp}.
+ LongSparseLongArray alreadySetAppOps = new LongSparseLongArray();
+
final int allowCount = mOpsToAllow.size();
for (int i = 0; i < allowCount; i++) {
final OpToChange op = mOpsToAllow.get(i);
+
setUidModeAllowed(op.code, op.uid, op.packageName);
+ alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
+
final int allowIfDefaultCount = mOpsToAllowIfDefault.size();
for (int i = 0; i < allowIfDefaultCount; i++) {
final OpToChange op = mOpsToAllowIfDefault.get(i);
- setUidModeAllowedIfDefault(op.code, op.uid, op.packageName);
+ if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+ continue;
+ }
+
+ boolean wasSet = setUidModeAllowedIfDefault(op.code, op.uid, op.packageName);
+ if (wasSet) {
+ alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
+ }
}
- final int foregroundCount = mOpsToForegroundIfAllow.size();
- for (int i = 0; i < foregroundCount; i++) {
+
+ final int foregroundIfAllowedCount = mOpsToForegroundIfAllow.size();
+ for (int i = 0; i < foregroundIfAllowedCount; i++) {
final OpToChange op = mOpsToForegroundIfAllow.get(i);
- setUidModeForegroundIfAllow(op.code, op.uid, op.packageName);
+ if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+ continue;
+ }
+
+ boolean wasSet = setUidModeForegroundIfAllow(op.code, op.uid, op.packageName);
+ if (wasSet) {
+ alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
+ }
}
- final int foregroundIfAllowCount = mOpsToForeground.size();
- for (int i = 0; i < foregroundIfAllowCount; i++) {
+
+ final int foregroundCount = mOpsToForeground.size();
+ for (int i = 0; i < foregroundCount; i++) {
final OpToChange op = mOpsToForeground.get(i);
+ if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+ continue;
+ }
+
setUidModeForeground(op.code, op.uid, op.packageName);
+ alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
+
final int ignoreCount = mOpsToIgnore.size();
for (int i = 0; i < ignoreCount; i++) {
final OpToChange op = mOpsToIgnore.get(i);
+ if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+ continue;
+ }
+
setUidModeIgnored(op.code, op.uid, op.packageName);
+ alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
+
final int ignoreIfDefaultCount = mOpsToIgnoreIfDefault.size();
for (int i = 0; i < ignoreIfDefaultCount; i++) {
final OpToChange op = mOpsToIgnoreIfDefault.get(i);
- setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName);
+ if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+ continue;
+ }
+
+ boolean wasSet = setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName);
+ if (wasSet) {
+ alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
+ }
}
+
final int defaultCount = mOpsToDefault.size();
for (int i = 0; i < defaultCount; i++) {
final OpToChange op = mOpsToDefault.get(i);
+ if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+ continue;
+ }
+
setUidModeDefault(op.code, op.uid, op.packageName);
+ alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
}
@@ -479,7 +547,7 @@
private void addOpIfRestricted(@NonNull PermissionInfo permissionInfo,
@NonNull PackageInfo pkg) {
final String permission = permissionInfo.name;
- final int opCode = AppOpsManager.permissionToOpCode(permission);
+ final int opCode = getSwitchOp(permission);
final int uid = pkg.applicationInfo.uid;
if (!permissionInfo.isRestricted()) {
@@ -581,7 +649,7 @@
}
final String permission = permissionInfo.name;
- final int opCode = AppOpsManager.permissionToOpCode(permission);
+ final int opCode = getSwitchOp(permission);
final String pkgName = pkg.packageName;
final int uid = pkg.applicationInfo.uid;
@@ -641,7 +709,7 @@
}
for (String permission : pkg.requestedPermissions) {
- final int opCode = AppOpsManager.permissionToOpCode(permission);
+ final int opCode = getSwitchOp(permission);
if (opCode == OP_NONE) {
continue;
}
@@ -658,24 +726,27 @@
}
}
- private void setUidModeAllowedIfDefault(int opCode, int uid, @NonNull String packageName) {
- setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_ALLOWED, packageName);
+ private boolean setUidModeAllowedIfDefault(int opCode, int uid,
+ @NonNull String packageName) {
+ return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_ALLOWED, packageName);
}
private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
setUidMode(opCode, uid, MODE_ALLOWED, packageName);
}
- private void setUidModeForegroundIfAllow(int opCode, int uid, @NonNull String packageName) {
- setUidModeIfMode(opCode, uid, MODE_ALLOWED, MODE_FOREGROUND, packageName);
+ private boolean setUidModeForegroundIfAllow(int opCode, int uid,
+ @NonNull String packageName) {
+ return setUidModeIfMode(opCode, uid, MODE_ALLOWED, MODE_FOREGROUND, packageName);
}
private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) {
setUidMode(opCode, uid, MODE_FOREGROUND, packageName);
}
- private void setUidModeIgnoredIfDefault(int opCode, int uid, @NonNull String packageName) {
- setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_IGNORED, packageName);
+ private boolean setUidModeIgnoredIfDefault(int opCode, int uid,
+ @NonNull String packageName) {
+ return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_IGNORED, packageName);
}
private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) {
@@ -692,14 +763,17 @@
}
}
- private void setUidModeIfMode(int opCode, int uid, int requiredModeBefore, int newMode,
+ private boolean setUidModeIfMode(int opCode, int uid, int requiredModeBefore, int newMode,
@NonNull String packageName) {
final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
.opToPublicName(opCode), uid, packageName);
if (currentMode == requiredModeBefore) {
mAppOpsManager.setUidMode(opCode, uid, newMode);
+ return true;
}
+
+ return false;
}
private void setUidModeDefault(int opCode, int uid, String packageName) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 510718f..602cc55 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4166,7 +4166,7 @@
case KeyEvent.KEYCODE_VOLUME_MUTE:
return mDefaultDisplayPolicy.getDockMode() != Intent.EXTRA_DOCK_STATE_UNDOCKED;
- // ignore media and camera keys
+ // ignore media keys
case KeyEvent.KEYCODE_MUTE:
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY:
@@ -4179,7 +4179,6 @@
case KeyEvent.KEYCODE_MEDIA_RECORD:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
- case KeyEvent.KEYCODE_CAMERA:
return false;
}
return true;
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index c7f8c07..c7e241b 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -41,6 +41,9 @@
"options": [
{
"include-filter": "android.permission.cts.SplitPermissionTest"
+ },
+ {
+ "include-filter": "android.permission.cts.BackgroundPermissionsTest"
}
]
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 01aeac8..ba61055 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -244,8 +244,6 @@
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
- // TODO(b/67864419): Remove once recents component is overridden
- private static final String LEGACY_RECENTS_PACKAGE_NAME = "com.android.systemui.recents";
private static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -1290,8 +1288,8 @@
// We only allow home activities to be resizeable if they explicitly requested it.
info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
}
- } else if (mActivityComponent.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
- || mAtmService.getRecentTasks().isRecentsComponent(mActivityComponent, appInfo.uid)) {
+ } else if (mAtmService.getRecentTasks().isRecentsComponent(mActivityComponent,
+ appInfo.uid)) {
activityType = ACTIVITY_TYPE_RECENTS;
} else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
&& canLaunchAssistActivity(launchedFromPackage)) {
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index ba23258..af72931 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -25,6 +25,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.NonNull;
import android.app.RemoteAction;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
@@ -45,6 +46,7 @@
import android.view.IPinnedStackListener;
import com.android.internal.policy.PipSnapAlgorithm;
+import com.android.internal.util.Preconditions;
import com.android.server.UiThread;
import java.io.PrintWriter;
@@ -326,8 +328,8 @@
boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) {
synchronized (mService.mGlobalLock) {
final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
- if (mDisplayInfo.equals(displayInfo)) {
- // We are already in the right orientation, ignore
+ if (isSameDimensionAndRotation(mDisplayInfo, displayInfo)) {
+ // No dimension/rotation change, ignore
outBounds.setEmpty();
return false;
} else if (targetBounds.isEmpty()) {
@@ -427,6 +429,15 @@
notifyActionsChanged(mActions);
}
+ private boolean isSameDimensionAndRotation(@NonNull DisplayInfo display1,
+ @NonNull DisplayInfo display2) {
+ Preconditions.checkNotNull(display1);
+ Preconditions.checkNotNull(display2);
+ return ((display1.rotation == display2.rotation)
+ && (display1.logicalWidth == display2.logicalWidth)
+ && (display1.logicalHeight == display2.logicalHeight));
+ }
+
/**
* Notifies listeners that the PIP needs to be adjusted for the IME.
*/
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 8d1cc71..9f8f265 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -612,4 +612,16 @@
boolean hasAlertWindowSurfaces() {
return !mAlertWindowSurfaces.isEmpty();
}
+
+ public void blessInputSurface(int displayId, SurfaceControl surface,
+ InputChannel outInputChannel) {
+ final int callerUid = Binder.getCallingUid();
+ final int callerPid = Binder.getCallingPid();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mService.blessInputSurface(callerUid, callerPid, displayId, surface, outInputChannel);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3820106..fd86faa 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -348,6 +348,9 @@
void onDisplayChanged(DisplayContent dc) {
adjustBoundsForDisplayChangeIfNeeded(dc);
super.onDisplayChanged(dc);
+ final int displayId = (dc != null) ? dc.getDisplayId() : Display.INVALID_DISPLAY;
+ mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
+ mTaskId, displayId);
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 27175c7..f776062 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -55,6 +55,7 @@
private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
private static final int NOTIFY_SINGLE_TASK_DISPLAY_DRAWN = 22;
+ private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 23;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -159,6 +160,10 @@
l.onSingleTaskDisplayDrawn(m.arg1);
};
+ private final TaskStackConsumer mNotifyTaskDisplayChanged = (l, m) -> {
+ l.onTaskDisplayChanged(m.arg1, m.arg2);
+ };
+
@FunctionalInterface
public interface TaskStackConsumer {
void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -241,6 +246,9 @@
case NOTIFY_SINGLE_TASK_DISPLAY_DRAWN:
forAllRemoteListeners(mNotifySingleTaskDisplayDrawn, msg);
break;
+ case NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG:
+ forAllRemoteListeners(mNotifyTaskDisplayChanged, msg);
+ break;
}
}
}
@@ -495,4 +503,14 @@
forAllLocalListeners(mNotifySingleTaskDisplayDrawn, msg);
msg.sendToTarget();
}
+
+ /**
+ * Notify listeners that a task is reparented to another display.
+ */
+ void notifyTaskDisplayChanged(int taskId, int newDisplayId) {
+ final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG,
+ taskId, newDisplayId);
+ forAllLocalListeners(mNotifyTaskStackChanged, msg);
+ msg.sendToTarget();
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 76dd7be..e2d0acd 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -278,7 +278,7 @@
// an existing task.
adjustBoundsToAvoidConflictInDisplay(display, outParams.mBounds);
}
- } else {
+ } else if (display.inFreeformWindowingMode()) {
if (source != null && source.inFreeformWindowingMode()
&& resolvedMode == WINDOWING_MODE_FREEFORM
&& outParams.mBounds.isEmpty()
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index ddf25e6..eb45e73 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1931,8 +1931,12 @@
public boolean setPinnedStackAlpha(float alpha) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
synchronized (mWmService.mGlobalLock) {
- getPendingTransaction().setAlpha(getSurfaceControl(),
- mCancelCurrentBoundsAnimation ? 1 : alpha);
+ final SurfaceControl sc = getSurfaceControl();
+ if (sc == null || !sc.isValid()) {
+ // If the stack is already removed, don't bother updating any stack animation
+ return false;
+ }
+ getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha);
scheduleAnimation();
return !mCancelCurrentBoundsAnimation;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2bf24f6..6750ece1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -216,6 +216,7 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
+import android.view.InputWindowHandle;
import android.view.InsetsState;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
@@ -7748,4 +7749,53 @@
0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */);
}
}
+
+ /**
+ * Assigns an InputChannel to a SurfaceControl and configures it to receive
+ * touch input according to it's on-screen geometry.
+ *
+ * Used by WindowlessWindowManager to enable input on SurfaceControl embedded
+ * views.
+ */
+ void blessInputSurface(int callingUid, int callingPid, int displayId, SurfaceControl surface,
+ InputChannel outInputChannel) {
+ String name = "Blessed Surface";
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
+ InputChannel inputChannel = inputChannels[0];
+ InputChannel clientChannel = inputChannels[1];
+
+ clientChannel.transferTo(outInputChannel);
+ clientChannel.dispose();
+
+ IBinder token = new Binder();
+ mInputManager.registerInputChannel(inputChannel, token);
+
+ // Prevent the java finalizer from breaking the input channel. But we won't
+ // do any further management so we just release the java ref and let the
+ // InputDispatcher hold the last ref.
+ inputChannel.release();
+
+ InputWindowHandle h = new InputWindowHandle(null, null, displayId);
+ h.token = token;
+ h.name = name;
+ h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ h.layoutParamsType = 0;
+ h.dispatchingTimeoutNanos = -1;
+ h.canReceiveKeys = false;
+ h.hasFocus = false;
+ h.hasWallpaper = false;
+ h.paused = false;
+
+ h.ownerUid = callingUid;
+ h.ownerPid = callingPid;
+
+ h.inputFeatures = 0;
+
+ h.replaceTouchableRegionWithCrop(null);
+
+ SurfaceSession s = new SurfaceSession();
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setInputWindowInfo(surface, h);
+ t.apply();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index de68036..424142c 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -23,7 +23,12 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import org.mockito.stubbing.Answer;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@@ -56,7 +61,8 @@
public final class AmbientLuxTest {
private static final int AMBIENT_COLOR_TYPE = 20705;
private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc";
- private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5700;
+ private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5432.1f;
+ private static final float HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE = 3456.7f;
private Handler mHandler = new Handler(Looper.getMainLooper());
private Sensor mLightSensor;
@@ -68,6 +74,8 @@
@Mock private TypedArray mBrightnesses;
@Mock private TypedArray mBiases;
+ @Mock private TypedArray mHighLightBrightnesses;
+ @Mock private TypedArray mHighLightBiases;
@Before
public void setUp() throws Exception {
@@ -89,9 +97,10 @@
when(mResourcesSpy.getInteger(
R.integer.config_displayWhiteBalanceIncreaseDebounce))
.thenReturn(0);
- when(mResourcesSpy.getFloat(
- R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperature))
- .thenReturn(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE);
+ mockResourcesFloat(R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperature,
+ LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE);
+ mockResourcesFloat(R.dimen.config_displayWhiteBalanceHighLightAmbientColorTemperature,
+ HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceAmbientColorTemperatures))
.thenReturn(createTypedArray());
@@ -105,6 +114,13 @@
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceLowLightAmbientBiases))
.thenReturn(mBiases);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceHighLightAmbientBrightnesses))
+ .thenReturn(mHighLightBrightnesses);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceHighLightAmbientBiases))
+ .thenReturn(mHighLightBiases);
+ mockThrottler();
}
@Test
@@ -216,16 +232,119 @@
}
@Test
- public void testSpline_InvalidBiases() throws Exception {
- float[][] invalidBrightnesses =
- {{10.0f, 1000.0f}, {10.0f, 1000.0f}, {10.0f, 1000.0f}, {10.0f, 1000.0f},
- {10.0f, 1000.0f}, {-1.0f, 1.0f}, {-1.0f, 1.0f}};
- float[][] invalidBiases =
- {{0.0f, 2.0f}, {0.0f, 0.9f}, {0.1f, 1.0f}, {-0.1f, 1.0f},
- {0.1f, 1.1f}, {0.0f, 1.0f}, {-2.0f, 1.0f}};
- for (int i = 0; i < invalidBrightnesses.length; ++i) {
- setBrightnesses(invalidBrightnesses[i][0], invalidBrightnesses[i][1]);
- setBiases(invalidBiases[i][0], invalidBiases[i][1]);
+ public void testSpline_InvalidEndBias() throws Exception {
+ setBrightnesses(10.0f, 1000.0f);
+ setBiases(0.0f, 2.0f);
+
+ DisplayWhiteBalanceController controller =
+ DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+ final float ambientColorTemperature = 8000.0f;
+ setEstimatedColorTemperature(controller, ambientColorTemperature);
+ controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+ for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
+ setEstimatedBrightnessAndUpdate(controller, luxOverride);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ ambientColorTemperature, 0.001);
+ }
+ }
+
+ @Test
+ public void testSpline_InvalidBeginBias() throws Exception {
+ setBrightnesses(10.0f, 1000.0f);
+ setBiases(0.1f, 1.0f);
+
+ DisplayWhiteBalanceController controller =
+ DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+ final float ambientColorTemperature = 8000.0f;
+ setEstimatedColorTemperature(controller, ambientColorTemperature);
+ controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+ for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
+ setEstimatedBrightnessAndUpdate(controller, luxOverride);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ ambientColorTemperature, 0.001);
+ }
+ }
+
+ @Test
+ public void testSpline_OneSegmentHighLight() throws Exception {
+ final float lowerBrightness = 10.0f;
+ final float upperBrightness = 50.0f;
+ setHighLightBrightnesses(lowerBrightness, upperBrightness);
+ setHighLightBiases(0.0f, 1.0f);
+
+ DisplayWhiteBalanceController controller =
+ DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+ final float ambientColorTemperature = 8000.0f;
+ setEstimatedColorTemperature(controller, ambientColorTemperature);
+ controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+ for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
+ setEstimatedBrightnessAndUpdate(controller,
+ mix(lowerBrightness, upperBrightness, t));
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - t),
+ 0.001);
+ }
+
+ setEstimatedBrightnessAndUpdate(controller, upperBrightness + 1.0f);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
+
+ setEstimatedBrightnessAndUpdate(controller, 0.0f);
+ assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
+ }
+
+ @Test
+ public void testSpline_TwoSegmentsHighLight() throws Exception {
+ final float brightness0 = 10.0f;
+ final float brightness1 = 50.0f;
+ final float brightness2 = 60.0f;
+ setHighLightBrightnesses(brightness0, brightness1, brightness2);
+ final float bias0 = 0.0f;
+ final float bias1 = 0.25f;
+ final float bias2 = 1.0f;
+ setHighLightBiases(bias0, bias1, bias2);
+
+ DisplayWhiteBalanceController controller =
+ DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+ final float ambientColorTemperature = 6000.0f;
+ setEstimatedColorTemperature(controller, ambientColorTemperature);
+ controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+ for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
+ float luxOverride = mix(brightness0, brightness1, t);
+ setEstimatedBrightnessAndUpdate(controller, luxOverride);
+ float bias = mix(bias0, bias1, t);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - bias),
+ 0.01);
+ }
+
+ for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
+ float luxOverride = mix(brightness1, brightness2, t);
+ setEstimatedBrightnessAndUpdate(controller, luxOverride);
+ float bias = mix(bias1, bias2, t);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - bias),
+ 0.01);
+ }
+
+ setEstimatedBrightnessAndUpdate(controller, brightness2 + 1.0f);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
+
+ setEstimatedBrightnessAndUpdate(controller, 0.0f);
+ assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
+ }
+
+ @Test
+ public void testSpline_InvalidCombinations() throws Exception {
+ setBrightnesses(100.0f, 200.0f);
+ setBiases(0.0f, 1.0f);
+ setHighLightBrightnesses(150.0f, 250.0f);
+ setHighLightBiases(0.0f, 1.0f);
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
@@ -238,7 +357,44 @@
assertEquals(controller.mPendingAmbientColorTemperature,
ambientColorTemperature, 0.001);
}
- }
+ }
+
+ void mockThrottler() {
+ when(mResourcesSpy.getInteger(
+ R.integer.config_displayWhiteBalanceDecreaseDebounce)).thenReturn(0);
+ when(mResourcesSpy.getInteger(
+ R.integer.config_displayWhiteBalanceIncreaseDebounce)).thenReturn(0);
+ TypedArray base = mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceBaseThresholds);
+ TypedArray inc = mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceIncreaseThresholds);
+ TypedArray dec = mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceDecreaseThresholds);
+ base = spy(base);
+ inc = spy(inc);
+ dec = spy(dec);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceBaseThresholds)).thenReturn(base);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceIncreaseThresholds)).thenReturn(inc);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceDecreaseThresholds)).thenReturn(dec);
+ setFloatArrayResource(base, new float[]{0.0f});
+ setFloatArrayResource(inc, new float[]{0.0f});
+ setFloatArrayResource(dec, new float[]{0.0f});
+ }
+
+ private void mockResourcesFloat(int id, float floatValue) {
+ doAnswer(new Answer<Void>() {
+ public Void answer(InvocationOnMock invocation) {
+ TypedValue value = (TypedValue)invocation.getArgument(1);
+ value.type = TypedValue.TYPE_FLOAT;
+ value.data = Float.floatToIntBits(floatValue);
+ return null;
+ }
+ }).when(mResourcesSpy).getValue(
+ eq(id),
+ any(TypedValue.class), eq(true));
}
private void setEstimatedColorTemperature(DisplayWhiteBalanceController controller,
@@ -262,6 +418,14 @@
setFloatArrayResource(mBiases, vals);
}
+ private void setHighLightBrightnesses(float... vals) {
+ setFloatArrayResource(mHighLightBrightnesses, vals);
+ }
+
+ private void setHighLightBiases(float... vals) {
+ setFloatArrayResource(mHighLightBiases, vals);
+ }
+
private void setFloatArrayResource(TypedArray array, float[] vals) {
when(array.length()).thenReturn(vals.length);
for (int i = 0; i < vals.length; i++) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 366acea..d0ee634 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -162,6 +162,9 @@
}
final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
+ final TaskChangeNotificationController taskChangeNotificationController = mock(
+ TaskChangeNotificationController.class);
+ doReturn(taskChangeNotificationController).when(atms).getTaskChangeNotificationController();
final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
doReturn(wmLock).when(atms).getGlobalLock();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 24ff07d..c4b0a80 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1196,7 +1196,7 @@
mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
// Returned bounds with in fullscreen mode will be set to last non-fullscreen bounds.
- assertEquals(new Rect(0, 0, 200, 100), mCurrent.mBounds);
+ assertEquals(new Rect(0, 0, 200, 100), mResult.mBounds);
}
@Test
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index 3adc05e..7a1678a 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -18,6 +18,9 @@
name: "startop_test_app",
srcs: [
"src/EmptyActivity.java",
- "src/LayoutInflation.java",
+ "src/LayoutInflationActivity.java",
+ "src/ComplexLayoutInflationActivity.java",
+ "src/FrameLayoutInflationActivity.java",
+ "src/TextViewInflationActivity.java",
],
}
diff --git a/startop/apps/test/AndroidManifest.xml b/startop/apps/test/AndroidManifest.xml
index 6b08118..467d8f7 100644
--- a/startop/apps/test/AndroidManifest.xml
+++ b/startop/apps/test/AndroidManifest.xml
@@ -23,7 +23,12 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true">
- <activity android:name=".EmptyActivity">
+
+ <activity
+ android:label="Complex Layout Test"
+ android:name=".ComplexLayoutInflationActivity"
+ android:exported="true" >
+
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -31,7 +36,41 @@
</intent-filter>
</activity>
- <activity android:name=".LayoutInflation" android:exported="true" />
+ <activity
+ android:label="Empty Activity Layout Test"
+ android:name=".EmptyActivity"
+ android:exported="true" >
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:label="FrameLayout Layout Test"
+ android:name=".FrameLayoutInflationActivity"
+ android:exported="true" >
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:label="TextView Layout Test"
+ android:name=".TextViewInflationActivity"
+ android:exported="true" >
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/startop/apps/test/README.md b/startop/apps/test/README.md
index 53c184b..dadc66a 100644
--- a/startop/apps/test/README.md
+++ b/startop/apps/test/README.md
@@ -23,4 +23,4 @@
spent in view inflation to make it easier to focus on the time spent in view
inflation.
- adb shell am start -n com.android.startop.test/.LayoutInflation
+ adb shell am start -n com.android.startop.test/.ComplexLayoutInflationActivity
diff --git a/startop/apps/test/res/layout/framelayout_list.xml b/startop/apps/test/res/layout/framelayout_list.xml
new file mode 100644
index 0000000..2dd8219
--- /dev/null
+++ b/startop/apps/test/res/layout/framelayout_list.xml
@@ -0,0 +1,5013 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ffaaaaaa" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:background="#ff000000" />
+ </LinearLayout>
+</ScrollView>
diff --git a/startop/apps/test/res/layout/textview_list.xml b/startop/apps/test/res/layout/textview_list.xml
new file mode 100644
index 0000000..1cff5b2
--- /dev/null
+++ b/startop/apps/test/res/layout/textview_list.xml
@@ -0,0 +1,5014 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ </LinearLayout>
+</ScrollView>
diff --git a/startop/apps/test/src/ComplexLayoutInflationActivity.java b/startop/apps/test/src/ComplexLayoutInflationActivity.java
new file mode 100644
index 0000000..a357073
--- /dev/null
+++ b/startop/apps/test/src/ComplexLayoutInflationActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import android.os.Bundle;
+
+/**
+ * This activity inflates a reasonably complex layout to see the impact of
+ * layout inflation. The layout is supported by the viewcompiler, so this can be
+ * used for testing precompiled layout performance.
+ */
+public class ComplexLayoutInflationActivity extends LayoutInflationActivity {
+ protected void onCreate(Bundle savedInstanceState) {
+ Bundle newState = savedInstanceState == null
+ ? new Bundle() : new Bundle(savedInstanceState);
+ newState.putInt(LAYOUT_ID, R.layout.activity_main);
+
+ super.onCreate(newState);
+ }
+}
diff --git a/startop/apps/test/src/EmptyActivity.java b/startop/apps/test/src/EmptyActivity.java
index 5202938..bcb2e70 100644
--- a/startop/apps/test/src/EmptyActivity.java
+++ b/startop/apps/test/src/EmptyActivity.java
@@ -1,18 +1,18 @@
-//
-// Copyright (C) 2019 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.
-//
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package com.android.startop.test;
diff --git a/startop/apps/test/src/FrameLayoutInflationActivity.java b/startop/apps/test/src/FrameLayoutInflationActivity.java
new file mode 100644
index 0000000..b995e79
--- /dev/null
+++ b/startop/apps/test/src/FrameLayoutInflationActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import android.os.Bundle;
+
+public class FrameLayoutInflationActivity extends LayoutInflationActivity {
+ protected void onCreate(Bundle savedInstanceState) {
+ Bundle newState = savedInstanceState == null
+ ? new Bundle() : new Bundle(savedInstanceState);
+ newState.putInt(LAYOUT_ID, R.layout.framelayout_list);
+
+ super.onCreate(newState);
+ }
+}
diff --git a/startop/apps/test/src/LayoutInflation.java b/startop/apps/test/src/LayoutInflation.java
deleted file mode 100644
index daa4e45..0000000
--- a/startop/apps/test/src/LayoutInflation.java
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package com.android.startop.test;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Trace;
-import android.view.LayoutInflater;
-import android.view.View;
-
-/**
- * This activity inflates a reasonably complex layout to see the impact of
- * layout inflation. The layout is supported by the viewcompiler, so this can be
- * used for testing precompiled layout performance.
- */
-public class LayoutInflation extends Activity {
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- LayoutInflater inflater = LayoutInflater.from(this);
- Trace.beginSection("inflate layout:activity_main");
- View view = inflater.inflate(R.layout.activity_main, /*root=*/null);
- Trace.endSection();
- setContentView(view);
- }
-}
diff --git a/startop/apps/test/src/LayoutInflationActivity.java b/startop/apps/test/src/LayoutInflationActivity.java
new file mode 100644
index 0000000..06a0570
--- /dev/null
+++ b/startop/apps/test/src/LayoutInflationActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.LayoutInflater;
+import android.view.View;
+
+public class LayoutInflationActivity extends Activity {
+ public static String LAYOUT_ID = "layout-id";
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ int layoutId = savedInstanceState.getInt(LAYOUT_ID);
+ String layoutName = getResources().getResourceEntryName(layoutId);
+
+ LayoutInflater inflater = LayoutInflater.from(this);
+ Trace.beginSection("inflate layout: " + layoutName);
+ View view = inflater.inflate(layoutId, /*root=*/null);
+ Trace.endSection();
+ setContentView(view);
+ }
+}
diff --git a/startop/apps/test/src/TextViewInflationActivity.java b/startop/apps/test/src/TextViewInflationActivity.java
new file mode 100644
index 0000000..30e308e
--- /dev/null
+++ b/startop/apps/test/src/TextViewInflationActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import android.os.Bundle;
+
+public class TextViewInflationActivity extends LayoutInflationActivity {
+ protected void onCreate(Bundle savedInstanceState) {
+ Bundle newState = savedInstanceState == null
+ ? new Bundle() : new Bundle(savedInstanceState);
+ newState.putInt(LAYOUT_ID, R.layout.textview_list);
+
+ super.onCreate(newState);
+ }
+}
diff --git a/startop/scripts/app_startup/lib/adb_utils.py b/startop/scripts/app_startup/lib/adb_utils.py
new file mode 100644
index 0000000..00e2e99
--- /dev/null
+++ b/startop/scripts/app_startup/lib/adb_utils.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+
+"""Helper util libraries for calling adb command line."""
+
+import os
+import sys
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(
+ os.path.abspath(__file__)))))
+import lib.cmd_utils as cmd_utils
+
+def logcat_save_timestamp() -> str:
+ """Gets the current logcat timestamp.
+
+ Returns:
+ A string of timestamp.
+ """
+ _, output = cmd_utils.run_adb_shell_command(
+ "date -u +\'%Y-%m-%d %H:%M:%S.%N\'")
+ return output
+
+def vm_drop_cache():
+ """Free pagecache and slab object."""
+ cmd_utils.run_adb_shell_command('echo 3 > /proc/sys/vm/drop_caches')
+
+def root():
+ """Roots adb and successive adb commands will run under root."""
+ cmd_utils.run_shell_command('adb root')
+
+def disable_selinux():
+ """Disables selinux setting."""
+ _, output = cmd_utils.run_adb_shell_command('getenforce')
+ if output == 'Permissive':
+ return
+
+ print('Disable selinux permissions and restart framework.')
+ cmd_utils.run_adb_shell_command('setenforce 0')
+ cmd_utils.run_adb_shell_command('stop')
+ cmd_utils.run_adb_shell_command('start')
+ cmd_utils.run_shell_command('adb wait-for-device')
+
+def pkill(procname: str):
+ """Kills a process in device by its package name."""
+ _, pids = cmd_utils.run_shell_command('adb shell ps | grep "{}" | '
+ 'awk \'{{print $2;}}\''.
+ format(procname))
+
+ for pid in pids.split('\n'):
+ cmd_utils.run_adb_shell_command('kill {}'.format(pid))
diff --git a/startop/scripts/app_startup/run_app_with_prefetch.py b/startop/scripts/app_startup/run_app_with_prefetch.py
new file mode 100644
index 0000000..c7970f5
--- /dev/null
+++ b/startop/scripts/app_startup/run_app_with_prefetch.py
@@ -0,0 +1,320 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+
+"""Runner of one test given a setting.
+
+Run app and gather the measurement in a certain configuration.
+Print the result to stdout.
+See --help for more details.
+
+Sample usage:
+ $> ./python run_app_with_prefetch.py -p com.android.settings -a
+ com.android.settings.Settings -r fadvise -i input
+
+"""
+
+import argparse
+import os
+import sys
+import time
+from typing import List, Tuple
+from pathlib import Path
+
+# local imports
+import lib.adb_utils as adb_utils
+
+# global variables
+DIR = os.path.abspath(os.path.dirname(__file__))
+IORAP_COMMON_BASH_SCRIPT = os.path.realpath(os.path.join(DIR,
+ '../iorap/common'))
+
+sys.path.append(os.path.dirname(DIR))
+import lib.print_utils as print_utils
+import lib.cmd_utils as cmd_utils
+import iorap.lib.iorapd_utils as iorapd_utils
+
+def parse_options(argv: List[str] = None):
+ """Parses command line arguments and return an argparse Namespace object."""
+ parser = argparse.ArgumentParser(
+ description='Run an Android application once and measure startup time.'
+ )
+
+ required_named = parser.add_argument_group('required named arguments')
+ required_named.add_argument('-p', '--package', action='store', dest='package',
+ help='package of the application', required=True)
+
+ # optional arguments
+ # use a group here to get the required arguments to appear 'above' the
+ # optional arguments in help.
+ optional_named = parser.add_argument_group('optional named arguments')
+ optional_named.add_argument('-a', '--activity', action='store',
+ dest='activity',
+ help='launch activity of the application')
+ optional_named.add_argument('-s', '--simulate', dest='simulate',
+ action='store_true',
+ help='simulate the process without executing '
+ 'any shell commands')
+ optional_named.add_argument('-d', '--debug', dest='debug',
+ action='store_true',
+ help='Add extra debugging output')
+ optional_named.add_argument('-i', '--input', action='store', dest='input',
+ help='perfetto trace file protobuf',
+ default='TraceFile.pb')
+ optional_named.add_argument('-r', '--readahead', action='store',
+ dest='readahead',
+ help='which readahead mode to use',
+ default='cold',
+ choices=('warm', 'cold', 'mlock', 'fadvise'))
+ optional_named.add_argument('-t', '--timeout', dest='timeout', action='store',
+ type=int,
+ help='Timeout after this many seconds when '
+ 'executing a single run.',
+ default=10)
+ optional_named.add_argument('--compiler-filter', dest='compiler_filter',
+ action='store',
+ help='Which compiler filter to use.',
+ default=None)
+
+ return parser.parse_args(argv)
+
+def validate_options(opts: argparse.Namespace) -> bool:
+ """Validates the activity and trace file if needed.
+
+ Returns:
+ A bool indicates whether the activity is valid and trace file exists if
+ necessary.
+ """
+ needs_trace_file = (opts.readahead != 'cold' and opts.readahead != 'warm')
+ if needs_trace_file and (opts.input is None or
+ not os.path.exists(opts.input)):
+ print_utils.error_print('--input not specified!')
+ return False
+
+ # Install necessary trace file.
+ if needs_trace_file:
+ passed = iorapd_utils.iorapd_compiler_install_trace_file(
+ opts.package, opts.activity, opts.input)
+ if not cmd_utils.SIMULATE and not passed:
+ print_utils.error_print('Failed to install compiled TraceFile.pb for '
+ '"{}/{}"'.
+ format(opts.package, opts.activity))
+ return False
+
+ if opts.activity is not None:
+ return True
+
+ _, opts.activity = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'get_activity_name',
+ [opts.package])
+
+ if not opts.activity:
+ print_utils.error_print('Activity name could not be found, '
+ 'invalid package name?!')
+ return False
+
+ return True
+
+def set_up_adb_env():
+ """Sets up adb environment."""
+ adb_utils.root()
+ adb_utils.disable_selinux()
+ time.sleep(1)
+
+def configure_compiler_filter(compiler_filter: str, package: str,
+ activity: str) -> bool:
+ """Configures compiler filter (e.g. speed).
+
+ Returns:
+ A bool indicates whether configure of compiler filer succeeds or not.
+ """
+ if not compiler_filter:
+ print_utils.debug_print('No --compiler-filter specified, don\'t'
+ ' need to force it.')
+ return True
+
+ passed, current_compiler_filter_info = \
+ cmd_utils.run_shell_command(
+ '{} --package {}'.format(os.path.join(DIR, 'query_compiler_filter.py'),
+ package))
+
+ if passed != 0:
+ return passed
+
+ # TODO: call query_compiler_filter directly as a python function instead of
+ # these shell calls.
+ current_compiler_filter, current_reason, current_isa = current_compiler_filter_info.split(' ')
+ print_utils.debug_print('Compiler Filter={} Reason={} Isa={}'.format(
+ current_compiler_filter, current_reason, current_isa))
+
+ # Don't trust reasons that aren't 'unknown' because that means
+ # we didn't manually force the compilation filter.
+ # (e.g. if any automatic system-triggered compilations are not unknown).
+ if current_reason != 'unknown' or current_compiler_filter != compiler_filter:
+ passed, _ = adb_utils.run_shell_command('{}/force_compiler_filter '
+ '--compiler-filter "{}" '
+ '--package "{}"'
+ ' --activity "{}'.
+ format(DIR, compiler_filter,
+ package, activity))
+ else:
+ adb_utils.debug_print('Queried compiler-filter matched requested '
+ 'compiler-filter, skip forcing.')
+ passed = False
+ return passed
+
+def parse_metrics_output(input: str,
+ simulate: bool = False) -> List[Tuple[str, str, str]]:
+ """Parses ouput of app startup to metrics and corresponding values.
+
+ It converts 'a=b\nc=d\ne=f\n...' into '[(a,b,''),(c,d,''),(e,f,'')]'
+
+ Returns:
+ A list of tuples that including metric name, metric value and rest info.
+ """
+ if simulate:
+ return [('TotalTime', '123')]
+
+ all_metrics = []
+ for line in input.split('\n'):
+ if not line:
+ continue
+ splits = line.split('=')
+ if len(splits) < 2:
+ print_utils.error_print('Bad line "{}"'.format(line))
+ continue
+ metric_name = splits[0]
+ metric_value = splits[1]
+ rest = splits[2] if len(splits) > 2 else ''
+ if rest:
+ print_utils.error_print('Corrupt line "{}"'.format(line))
+ print_utils.debug_print('metric: "{metric_name}", '
+ 'value: "{metric_value}" '.
+ format(metric_name=metric_name,
+ metric_value=metric_value))
+
+ all_metrics.append((metric_name, metric_value))
+ return all_metrics
+
+def run(readahead: str,
+ package: str,
+ activity: str,
+ timeout: int,
+ simulate: bool,
+ debug: bool) -> List[Tuple[str, str]]:
+ """Runs app startup test.
+
+ Returns:
+ A list of tuples that including metric name, metric value and rest info.
+ """
+ print_utils.debug_print('==========================================')
+ print_utils.debug_print('===== START =====')
+ print_utils.debug_print('==========================================')
+
+ if readahead != 'warm':
+ print_utils.debug_print('Drop caches for non-warm start.')
+ # Drop all caches to get cold starts.
+ adb_utils.vm_drop_cache()
+
+ print_utils.debug_print('Running with timeout {}'.format(timeout))
+
+ pre_launch_timestamp = adb_utils.logcat_save_timestamp()
+
+ passed, output = cmd_utils.run_shell_command('timeout {timeout} '
+ '"{DIR}/launch_application" '
+ '"{package}" '
+ '"{activity}" | '
+ '"{DIR}/parse_metrics" '
+ '--package {package} '
+ '--activity {activity} '
+ '--timestamp "{timestamp}"'
+ .format(timeout=timeout,
+ DIR=DIR,
+ package=package,
+ activity=activity,
+ timestamp=pre_launch_timestamp))
+
+ if not output and not simulate:
+ return None
+
+ results = parse_metrics_output(output, simulate)
+
+ passed = perform_post_launch_cleanup(
+ readahead, package, activity, timeout, debug, pre_launch_timestamp)
+ if not passed and not simulate:
+ print_utils.error_print('Cannot perform post launch cleanup!')
+ return None
+
+ adb_utils.pkill(package)
+ return results
+
+def perform_post_launch_cleanup(readahead: str,
+ package: str,
+ activity: str,
+ timeout: int,
+ debug: bool,
+ logcat_timestamp: str) -> bool:
+ """Performs cleanup at the end of each loop iteration.
+
+ Returns:
+ A bool indicates whether the cleanup succeeds or not.
+ """
+ if readahead != 'warm' and readahead != 'cold':
+ return iorapd_utils.wait_for_iorapd_finish(package,
+ activity,
+ timeout,
+ debug,
+ logcat_timestamp)
+ return passed
+ # Don't need to do anything for warm or cold.
+ return True
+
+def run_test(opts: argparse.Namespace) -> List[Tuple[str, str]]:
+ """Runs one test using given options.
+
+ Returns:
+ A list of tuples that including metric name, metric value and anything left.
+ """
+ print_utils.DEBUG = opts.debug
+ cmd_utils.SIMULATE = opts.simulate
+
+ passed = validate_options(opts)
+ if not passed:
+ return None
+
+ set_up_adb_env()
+
+ # Ensure the APK is currently compiled with whatever we passed in
+ # via --compiler-filter.
+ # No-op if this option was not passed in.
+ if not configure_compiler_filter(opts.compiler_filter, opts.package,
+ opts.activity):
+ return None
+
+ return run(opts.readahead, opts.package, opts.activity, opts.timeout,
+ opts.simulate, opts.debug)
+
+def main():
+ args = parse_options()
+ result = run_test(args)
+
+ if result is None:
+ return 1
+
+ print(result)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/startop/scripts/app_startup/run_app_with_prefetch_test.py b/startop/scripts/app_startup/run_app_with_prefetch_test.py
new file mode 100644
index 0000000..241aea4
--- /dev/null
+++ b/startop/scripts/app_startup/run_app_with_prefetch_test.py
@@ -0,0 +1,270 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+#
+"""Unit tests for the run_app_with_prefetch_test.py script.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> ./run_app_with_prefetch_test.py
+ $> pytest run_app_with_prefetch_test.py
+ $> python -m pytest run_app_with_prefetch_test.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+
+import io
+import os
+import shlex
+import sys
+# global imports
+from contextlib import contextmanager
+
+# pip imports
+import pytest
+# local imports
+import run_app_with_prefetch as run
+from mock import Mock, call, patch
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+#
+# Argument Parsing Helpers
+#
+
+@contextmanager
+def ignore_stdout_stderr():
+ """Ignore stdout/stderr output for duration of this context."""
+ old_stdout = sys.stdout
+ old_stderr = sys.stderr
+ sys.stdout = io.StringIO()
+ sys.stderr = io.StringIO()
+ try:
+ yield
+ finally:
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+
+@contextmanager
+def argparse_bad_argument(msg):
+ """Asserts that a SystemExit is raised when executing this context.
+
+ If the assertion fails, print the message 'msg'.
+ """
+ with pytest.raises(SystemExit, message=msg):
+ with ignore_stdout_stderr():
+ yield
+
+def assert_bad_argument(args, msg):
+ """Asserts that the command line arguments in 'args' are malformed.
+
+ Prints 'msg' if the assertion fails.
+ """
+ with argparse_bad_argument(msg):
+ parse_args(args)
+
+def parse_args(args):
+ """
+ :param args: command-line like arguments as a single string
+ :return: dictionary of parsed key/values
+ """
+ # "-a b -c d" => ['-a', 'b', '-c', 'd']
+ return vars(run.parse_options(shlex.split(args)))
+
+def default_dict_for_parsed_args(**kwargs):
+ """Combines it with all of the "optional" parameters' default values."""
+ d = {
+ 'readahead': 'cold',
+ 'simulate': None,
+ 'simulate': False,
+ 'debug': False,
+ 'input': 'TraceFile.pb',
+ 'timeout': 10,
+ 'compiler_filter': None,
+ 'activity': None
+ }
+ d.update(kwargs)
+ return d
+
+def default_mock_dict_for_parsed_args(include_optional=True, **kwargs):
+ """Combines default dict with all optional parameters with some mock required
+ parameters.
+ """
+ d = {'package': 'com.fake.package'}
+ if include_optional:
+ d.update(default_dict_for_parsed_args())
+ d.update(kwargs)
+ return d
+
+def parse_optional_args(str):
+ """
+ Parses an argument string which already includes all the required arguments
+ in default_mock_dict_for_parsed_args.
+ """
+ req = '--package com.fake.package'
+ return parse_args('%s %s' % (req, str))
+
+def test_argparse():
+ # missing arguments
+ assert_bad_argument('', '-p are required')
+
+ # required arguments are parsed correctly
+ ad = default_dict_for_parsed_args # assert dict
+ assert parse_args('--package xyz') == ad(package='xyz')
+
+ assert parse_args('-p xyz') == ad(package='xyz')
+
+ assert parse_args('-p xyz -s') == ad(package='xyz', simulate=True)
+ assert parse_args('-p xyz --simulate') == ad(package='xyz', simulate=True)
+
+ # optional arguments are parsed correctly.
+ mad = default_mock_dict_for_parsed_args # mock assert dict
+ assert parse_optional_args('--input trace.pb') == mad(input='trace.pb')
+
+ assert parse_optional_args('--compiler-filter speed') == \
+ mad(compiler_filter='speed')
+
+ assert parse_optional_args('-d') == mad(debug=True)
+ assert parse_optional_args('--debug') == mad(debug=True)
+
+ assert parse_optional_args('--timeout 123') == mad(timeout=123)
+ assert parse_optional_args('-t 456') == mad(timeout=456)
+
+ assert parse_optional_args('-r warm') == mad(readahead='warm')
+ assert parse_optional_args('--readahead warm') == mad(readahead='warm')
+
+ assert parse_optional_args('-a act') == mad(activity='act')
+ assert parse_optional_args('--activity act') == mad(activity='act')
+
+def test_main():
+ args = '--package com.fake.package --activity act -s'
+ opts = run.parse_options(shlex.split(args))
+
+ result = run.run_test(opts)
+ assert result == [('TotalTime', '123')]
+
+def test_set_up_adb_env():
+ with patch('lib.cmd_utils.run_shell_command',
+ new_callable=Mock) as mock_run_shell_command:
+ mock_run_shell_command.return_value = (True, '')
+ run.set_up_adb_env()
+
+ calls = [call('adb root'),
+ call('adb shell "getenforce"'),
+ call('adb shell "setenforce 0"'),
+ call('adb shell "stop"'),
+ call('adb shell "start"'),
+ call('adb wait-for-device')]
+ mock_run_shell_command.assert_has_calls(calls)
+
+def test_set_up_adb_env_with_permissive():
+ with patch('lib.cmd_utils.run_shell_command',
+ new_callable=Mock) as mock_run_shell_command:
+ mock_run_shell_command.return_value = (True, 'Permissive')
+ run.set_up_adb_env()
+
+ calls = [call('adb root'), call('adb shell "getenforce"')]
+ mock_run_shell_command.assert_has_calls(calls)
+
+def test_configure_compiler_filter():
+ with patch('lib.cmd_utils.run_shell_command',
+ new_callable=Mock) as mock_run_shell_command:
+ mock_run_shell_command.return_value = (True, 'speed arm64 kUpToDate')
+ run.configure_compiler_filter('speed', 'music', 'MainActivity')
+
+ calls = [call(os.path.join(run.DIR, 'query_compiler_filter.py') +
+ ' --package music')]
+ mock_run_shell_command.assert_has_calls(calls)
+
+def test_parse_metrics_output():
+ input = 'a1=b1\nc1=d1\ne1=f1'
+ ret = run.parse_metrics_output(input)
+
+ assert ret == [('a1', 'b1'), ('c1', 'd1'), ('e1', 'f1')]
+
+def _mocked_run_shell_command(*args, **kwargs):
+ if args[0] == 'adb shell "date -u +\'%Y-%m-%d %H:%M:%S.%N\'"':
+ return (True, "123:123")
+ elif args[0] == 'adb shell ps | grep "music" | awk \'{print $2;}\'':
+ return (True, '9999')
+ else:
+ return (True, 'a1=b1\nc1=d1=d2\ne1=f1')
+
+def test_run_no_vm_cache_drop():
+ with patch('lib.cmd_utils.run_shell_command',
+ new_callable=Mock) as mock_run_shell_command:
+ mock_run_shell_command.side_effect = _mocked_run_shell_command
+ run.run('warm',
+ 'music',
+ 'MainActivity',
+ timeout=10,
+ simulate=False,
+ debug=False)
+
+ calls = [call('adb shell "date -u +\'%Y-%m-%d %H:%M:%S.%N\'"'),
+ call(
+ 'timeout {timeout} "{DIR}/launch_application" "{package}" "{activity}" | '
+ '"{DIR}/parse_metrics" --package {package} --activity {activity} '
+ '--timestamp "{timestamp}"'
+ .format(timeout=10,
+ DIR=run.DIR,
+ package='music',
+ activity='MainActivity',
+ timestamp='123:123')),
+ call('adb shell ps | grep "music" | awk \'{print $2;}\''),
+ call('adb shell "kill 9999"')]
+ mock_run_shell_command.assert_has_calls(calls)
+
+def test_run_with_vm_cache_drop_and_post_launch_cleanup():
+ with patch('lib.cmd_utils.run_shell_command',
+ new_callable=Mock) as mock_run_shell_command:
+ mock_run_shell_command.side_effect = _mocked_run_shell_command
+ run.run('fadvise',
+ 'music',
+ 'MainActivity',
+ timeout=10,
+ simulate=False,
+ debug=False)
+
+ calls = [call('adb shell "echo 3 > /proc/sys/vm/drop_caches"'),
+ call('adb shell "date -u +\'%Y-%m-%d %H:%M:%S.%N\'"'),
+ call(
+ 'timeout {timeout} "{DIR}/launch_application" "{package}" "{activity}" | '
+ '"{DIR}/parse_metrics" --package {package} --activity {activity} '
+ '--timestamp "{timestamp}"'
+ .format(timeout=10,
+ DIR=run.DIR,
+ package='music',
+ activity='MainActivity',
+ timestamp='123:123')),
+ call(
+ 'bash -c "source {script_path}; '
+ 'iorapd_readahead_wait_until_finished '
+ '\'{package}\' \'{activity}\' \'{timestamp}\' \'{timeout}\'"'.
+ format(timeout=10,
+ package='music',
+ activity='MainActivity',
+ timestamp='123:123',
+ script_path=run.IORAP_COMMON_BASH_SCRIPT)),
+ call('adb shell ps | grep "music" | awk \'{print $2;}\''),
+ call('adb shell "kill 9999"')]
+ mock_run_shell_command.assert_has_calls(calls)
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/startop/scripts/iorap/lib/iorapd_utils.py b/startop/scripts/iorap/lib/iorapd_utils.py
new file mode 100644
index 0000000..f907305
--- /dev/null
+++ b/startop/scripts/iorap/lib/iorapd_utils.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+
+"""Helper util libraries for iorapd related operations."""
+
+import os
+import sys
+from pathlib import Path
+
+# up to two level, like '../../'
+sys.path.append(Path(os.path.abspath(__file__)).parents[2])
+import lib.cmd_utils as cmd_utils
+
+IORAPID_LIB_DIR = os.path.abspath(os.path.dirname(__file__))
+IORAPD_DATA_PATH = '/data/misc/iorapd'
+IORAP_COMMON_BASH_SCRIPT = os.path.realpath(os.path.join(IORAPID_LIB_DIR,
+ '../common'))
+
+def _iorapd_path_to_data_file(package: str, activity: str, suffix: str) -> str:
+ """Gets conventional data filename.
+
+ Returns:
+ The path of iorapd data file.
+
+ """
+ # Match logic of 'AppComponentName' in iorap::compiler C++ code.
+ return '{}/{}%2F{}.{}'.format(IORAPD_DATA_PATH, package, activity, suffix)
+
+def iorapd_compiler_install_trace_file(package: str, activity: str,
+ input_file: str) -> bool:
+ """Installs a compiled trace file.
+
+ Returns:
+ Whether the trace file is installed successful or not.
+ """
+ # remote path calculations
+ compiled_path = _iorapd_path_to_data_file(package, activity,
+ 'compiled_trace.pb')
+
+ if not os.path.exists(input_file):
+ print('Error: File {} does not exist'.format(input_file))
+ return False
+
+ passed, _ = cmd_utils.run_adb_shell_command(
+ 'mkdir -p "$(dirname "{}")"'.format(compiled_path))
+ if not passed:
+ return False
+
+ passed, _ = cmd_utils.run_shell_command('adb push "{}" "{}"'.format(
+ input_file, compiled_path))
+
+ return passed
+
+def wait_for_iorapd_finish(package: str,
+ activity: str,
+ timeout: int,
+ debug: bool,
+ logcat_timestamp: str)->bool:
+ """Waits for the finish of iorapd.
+
+ Returns:
+ A bool indicates whether the iorapd is done successfully or not.
+ """
+ # Set verbose for bash script based on debug flag.
+ if debug:
+ os.putenv('verbose', 'y')
+
+ # Validate that readahead completes.
+ # If this fails for some reason, then this will also discard the timing of
+ # the run.
+ passed, _ = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'iorapd_readahead_wait_until_finished',
+ [package, activity, logcat_timestamp,
+ str(timeout)])
+ return passed
diff --git a/startop/scripts/lib/cmd_utils.py b/startop/scripts/lib/cmd_utils.py
new file mode 100644
index 0000000..c3d9605
--- /dev/null
+++ b/startop/scripts/lib/cmd_utils.py
@@ -0,0 +1,166 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+
+"""Helper util libraries for command line operations."""
+
+import asyncio
+import sys
+import time
+from typing import Tuple, Optional, List
+
+import lib.print_utils as print_utils
+
+TIMEOUT = 50
+SIMULATE = False
+
+def run_adb_shell_command(cmd: str) -> Tuple[bool, str]:
+ """Runs command using adb shell.
+
+ Returns:
+ A tuple of running status (True=succeeded, False=failed or timed out) and
+ std output (string contents of stdout with trailing whitespace removed).
+ """
+ return run_shell_command('adb shell "{}"'.format(cmd))
+
+def run_shell_func(script_path: str,
+ func: str,
+ args: List[str]) -> Tuple[bool, str]:
+ """Runs shell function with default timeout.
+
+ Returns:
+ A tuple of running status (True=succeeded, False=failed or timed out) and
+ std output (string contents of stdout with trailing whitespace removed) .
+ """
+ cmd = 'bash -c "source {script_path}; {func} {args}"'.format(
+ script_path=script_path,
+ func=func,
+ args=' '.join("'{}'".format(arg) for arg in args))
+ print_utils.debug_print(cmd)
+ return run_shell_command(cmd)
+
+def run_shell_command(cmd: str) -> Tuple[bool, str]:
+ """Runs shell command with default timeout.
+
+ Returns:
+ A tuple of running status (True=succeeded, False=failed or timed out) and
+ std output (string contents of stdout with trailing whitespace removed) .
+ """
+ return execute_arbitrary_command([cmd],
+ TIMEOUT,
+ shell=True,
+ simulate=SIMULATE)
+
+def execute_arbitrary_command(cmd: List[str],
+ timeout: int,
+ shell: bool,
+ simulate: bool) -> Tuple[bool, str]:
+ """Run arbitrary shell command with default timeout.
+
+ Mostly copy from
+ frameworks/base/startop/scripts/app_startup/app_startup_runner.py.
+
+ Args:
+ cmd: list of cmd strings.
+ timeout: the time limit of running cmd.
+ shell: indicate if the cmd is a shell command.
+ simulate: if it's true, do not run the command and assume the running is
+ successful.
+
+ Returns:
+ A tuple of running status (True=succeeded, False=failed or timed out) and
+ std output (string contents of stdout with trailing whitespace removed) .
+ """
+ if simulate:
+ print(cmd)
+ return True, ''
+
+ print_utils.debug_print('[EXECUTE]', cmd)
+ # block until either command finishes or the timeout occurs.
+ loop = asyncio.get_event_loop()
+
+ (return_code, script_output) = loop.run_until_complete(
+ _run_command(*cmd, shell=shell, timeout=timeout))
+
+ script_output = script_output.decode() # convert bytes to str
+
+ passed = (return_code == 0)
+ print_utils.debug_print('[$?]', return_code)
+ if not passed:
+ print('[FAILED, code:%s]' % (return_code), script_output, file=sys.stderr)
+
+ return passed, script_output.rstrip()
+
+async def _run_command(*args: List[str],
+ shell: bool = False,
+ timeout: Optional[int] = None) -> Tuple[int, bytes]:
+ if shell:
+ process = await asyncio.create_subprocess_shell(
+ *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
+ else:
+ process = await asyncio.create_subprocess_exec(
+ *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
+
+ script_output = b''
+
+ print_utils.debug_print('[PID]', process.pid)
+
+ timeout_remaining = timeout
+ time_started = time.time()
+
+ # read line (sequence of bytes ending with b'\n') asynchronously
+ while True:
+ try:
+ line = await asyncio.wait_for(process.stdout.readline(),
+ timeout_remaining)
+ print_utils.debug_print('[STDOUT]', line)
+ script_output += line
+
+ if timeout_remaining:
+ time_elapsed = time.time() - time_started
+ timeout_remaining = timeout - time_elapsed
+ except asyncio.TimeoutError:
+ print_utils.debug_print('[TIMEDOUT] Process ', process.pid)
+
+ print_utils.debug_print('[TIMEDOUT] Sending SIGTERM.')
+ process.terminate()
+
+ # 5 second timeout for process to handle SIGTERM nicely.
+ try:
+ (remaining_stdout,
+ remaining_stderr) = await asyncio.wait_for(process.communicate(), 5)
+ script_output += remaining_stdout
+ except asyncio.TimeoutError:
+ print_utils.debug_print('[TIMEDOUT] Sending SIGKILL.')
+ process.kill()
+
+ # 5 second timeout to finish with SIGKILL.
+ try:
+ (remaining_stdout,
+ remaining_stderr) = await asyncio.wait_for(process.communicate(), 5)
+ script_output += remaining_stdout
+ except asyncio.TimeoutError:
+ # give up, this will leave a zombie process.
+ print_utils.debug_print('[TIMEDOUT] SIGKILL failed for process ',
+ process.pid)
+ time.sleep(100)
+
+ return -1, script_output
+ else:
+ if not line: # EOF
+ break
+
+ code = await process.wait() # wait for child process to exit
+ return code, script_output
diff --git a/startop/scripts/lib/print_utils.py b/startop/scripts/lib/print_utils.py
new file mode 100644
index 0000000..c33e0f9
--- /dev/null
+++ b/startop/scripts/lib/print_utils.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+
+"""Helper util libraries for debug printing."""
+
+import sys
+
+DEBUG = False
+
+def debug_print(*args, **kwargs):
+ """Prints the args to sys.stderr if the DEBUG is set."""
+ if DEBUG:
+ print(*args, **kwargs, file=sys.stderr)
+
+def error_print(*args, **kwargs):
+ print('[ERROR]:', *args, file=sys.stderr, **kwargs)
diff --git a/telecomm/java/android/telecom/ConferenceParticipant.java b/telecomm/java/android/telecom/ConferenceParticipant.java
index 2f1505c..5e4818a 100644
--- a/telecomm/java/android/telecom/ConferenceParticipant.java
+++ b/telecomm/java/android/telecom/ConferenceParticipant.java
@@ -19,6 +19,7 @@
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
@@ -69,18 +70,28 @@
private long mConnectElapsedTime;
/**
+ * The direction of the call;
+ * {@link Call.Details#DIRECTION_INCOMING} for incoming calls, or
+ * {@link Call.Details#DIRECTION_OUTGOING} for outgoing calls.
+ */
+ private int mCallDirection;
+
+ /**
* Creates an instance of {@code ConferenceParticipant}.
*
* @param handle The conference participant's handle (e.g., phone number).
* @param displayName The display name for the participant.
* @param endpoint The enpoint Uri which uniquely identifies this conference participant.
* @param state The state of the participant in the conference.
+ * @param callDirection The direction of the call (incoming/outgoing).
*/
- public ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state) {
+ public ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state,
+ int callDirection) {
mHandle = handle;
mDisplayName = displayName;
mEndpoint = endpoint;
mState = state;
+ mCallDirection = callDirection;
}
/**
@@ -96,7 +107,16 @@
String displayName = source.readString();
Uri endpoint = source.readParcelable(classLoader);
int state = source.readInt();
- return new ConferenceParticipant(handle, displayName, endpoint, state);
+ long connectTime = source.readLong();
+ long elapsedRealTime = source.readLong();
+ int callDirection = source.readInt();
+ ConferenceParticipant participant =
+ new ConferenceParticipant(handle, displayName, endpoint, state,
+ callDirection);
+ participant.setConnectTime(connectTime);
+ participant.setConnectElapsedTime(elapsedRealTime);
+ participant.setCallDirection(callDirection);
+ return participant;
}
@Override
@@ -170,6 +190,9 @@
dest.writeString(mDisplayName);
dest.writeParcelable(mEndpoint, 0);
dest.writeInt(mState);
+ dest.writeLong(mConnectTime);
+ dest.writeLong(mConnectElapsedTime);
+ dest.writeInt(mCallDirection);
}
/**
@@ -192,6 +215,8 @@
sb.append(getConnectTime());
sb.append(" ConnectElapsedTime: ");
sb.append(getConnectElapsedTime());
+ sb.append(" Direction: ");
+ sb.append(getCallDirection() == Call.Details.DIRECTION_INCOMING ? "Incoming" : "Outgoing");
sb.append("]");
return sb.toString();
}
@@ -239,7 +264,7 @@
}
/**
- * The connect elpased time of the participant to the conference.
+ * The connect elapsed time of the participant to the conference.
*/
public long getConnectElapsedTime() {
return mConnectElapsedTime;
@@ -248,4 +273,76 @@
public void setConnectElapsedTime(long connectElapsedTime) {
mConnectElapsedTime = connectElapsedTime;
}
+
+ /**
+ * @return The direction of the call (incoming/outgoing).
+ */
+ public @Call.Details.CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
+ /**
+ * Sets the direction of the call.
+ * @param callDirection Whether the call is incoming or outgoing.
+ */
+ public void setCallDirection(@Call.Details.CallDirection int callDirection) {
+ mCallDirection = callDirection;
+ }
+
+ /**
+ * Attempts to build a tel: style URI from a conference participant.
+ * Conference event package data contains SIP URIs, so we try to extract the phone number and
+ * format into a typical tel: style URI.
+ *
+ * @param address The conference participant's address.
+ * @param countryIso The country ISO of the current subscription; used when formatting the
+ * participant phone number to E.164 format.
+ * @return The participant's address URI.
+ * @hide
+ */
+ @VisibleForTesting
+ public static Uri getParticipantAddress(Uri address, String countryIso) {
+ if (address == null) {
+ return address;
+ }
+ // Even if address is already in tel: format, still parse it and rebuild.
+ // This is to recognize tel URIs such as:
+ // tel:6505551212;phone-context=ims.mnc012.mcc034.3gppnetwork.org
+
+ // Conference event package participants are identified using SIP URIs (see RFC3261).
+ // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+ // Per RFC3261, the "user" can be a telephone number.
+ // For example: sip:1650555121;phone-context=blah.com@host.com
+ // In this case, the phone number is in the user field of the URI, and the parameters can be
+ // ignored.
+ //
+ // A SIP URI can also specify a phone number in a format similar to:
+ // sip:+1-212-555-1212@something.com;user=phone
+ // In this case, the phone number is again in user field and the parameters can be ignored.
+ // We can get the user field in these instances by splitting the string on the @, ;, or :
+ // and looking at the first found item.
+ String number = address.getSchemeSpecificPart();
+ if (TextUtils.isEmpty(number)) {
+ return address;
+ }
+
+ String numberParts[] = number.split("[@;:]");
+ if (numberParts.length == 0) {
+ return address;
+ }
+ number = numberParts[0];
+
+ // Attempt to format the number in E.164 format and use that as part of the TEL URI.
+ // RFC2806 recommends to format telephone numbers using E.164 since it is independent of
+ // how the dialing of said numbers takes place.
+ // If conversion to E.164 fails, the returned value is null. In that case, fallback to the
+ // number which was in the CEP data.
+ String formattedNumber = null;
+ if (!TextUtils.isEmpty(countryIso)) {
+ formattedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ }
+
+ return Uri.fromParts(PhoneAccount.SCHEME_TEL,
+ formattedNumber != null ? formattedNumber : number, null);
+ }
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 47587c5..0983eea 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1793,6 +1793,11 @@
private ConnectionService mConnectionService;
private Bundle mExtras;
private final Object mExtrasLock = new Object();
+ /**
+ * The direction of the connection; used where an existing connection is created and we need to
+ * communicate to Telecom whether its incoming or outgoing.
+ */
+ private @Call.Details.CallDirection int mCallDirection = Call.Details.DIRECTION_UNKNOWN;
/**
* Tracks the key set for the extras bundle provided on the last invocation of
@@ -3357,4 +3362,21 @@
l.onConnectionEvent(this, event, extras);
}
}
+
+ /**
+ * @return The direction of the call.
+ * @hide
+ */
+ public final @Call.Details.CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
+ /**
+ * Sets the direction of this connection.
+ * @param callDirection The direction of this connection.
+ * @hide
+ */
+ public void setCallDirection(@Call.Details.CallDirection int callDirection) {
+ mCallDirection = callDirection;
+ }
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 626fcc4..3548810 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2144,7 +2144,8 @@
connection.getDisconnectCause(),
emptyList,
connection.getExtras(),
- conferenceId);
+ conferenceId,
+ connection.getCallDirection());
mAdapter.addExistingConnection(id, parcelableConnection);
}
}
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index dab1c6e..4734af6 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -53,6 +53,7 @@
private final List<String> mConferenceableConnectionIds;
private final Bundle mExtras;
private String mParentCallId;
+ private @Call.Details.CallDirection int mCallDirection;
/** @hide */
public ParcelableConnection(
@@ -75,13 +76,15 @@
DisconnectCause disconnectCause,
List<String> conferenceableConnectionIds,
Bundle extras,
- String parentCallId) {
+ String parentCallId,
+ @Call.Details.CallDirection int callDirection) {
this(phoneAccount, state, capabilities, properties, supportedAudioRoutes, address,
addressPresentation, callerDisplayName, callerDisplayNamePresentation,
videoProvider, videoState, ringbackRequested, isVoipAudioMode, connectTimeMillis,
connectElapsedTimeMillis, statusHints, disconnectCause, conferenceableConnectionIds,
extras);
mParentCallId = parentCallId;
+ mCallDirection = callDirection;
}
/** @hide */
@@ -125,6 +128,7 @@
mConferenceableConnectionIds = conferenceableConnectionIds;
mExtras = extras;
mParentCallId = null;
+ mCallDirection = Call.Details.DIRECTION_UNKNOWN;
}
public PhoneAccountHandle getPhoneAccount() {
@@ -219,6 +223,10 @@
return mParentCallId;
}
+ public @Call.Details.CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
@Override
public String toString() {
return new StringBuilder()
@@ -234,6 +242,8 @@
.append(mExtras)
.append(", parent:")
.append(mParentCallId)
+ .append(", callDirection:")
+ .append(mCallDirection)
.toString();
}
@@ -265,6 +275,7 @@
int supportedAudioRoutes = source.readInt();
String parentCallId = source.readString();
long connectElapsedTimeMillis = source.readLong();
+ int callDirection = source.readInt();
return new ParcelableConnection(
phoneAccount,
@@ -286,7 +297,8 @@
disconnectCause,
conferenceableConnectionIds,
extras,
- parentCallId);
+ parentCallId,
+ callDirection);
}
@Override
@@ -325,5 +337,6 @@
destination.writeInt(mSupportedAudioRoutes);
destination.writeString(mParentCallId);
destination.writeLong(mConnectElapsedTimeMillis);
+ destination.writeInt(mCallDirection);
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 209b345..22a8232 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4901,8 +4901,7 @@
ITelephony telephony = getITelephony();
if (telephony == null)
return DATA_ACTIVITY_NONE;
- return telephony.getDataActivity(
- getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
+ return telephony.getDataActivity();
} catch (RemoteException ex) {
// the phone process is restarting.
return DATA_ACTIVITY_NONE;
@@ -4950,8 +4949,7 @@
ITelephony telephony = getITelephony();
if (telephony == null)
return DATA_DISCONNECTED;
- return telephony.getDataState(
- getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
+ return telephony.getDataState();
} catch (RemoteException ex) {
// the phone process is restarting.
return DATA_DISCONNECTED;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e23cd68..5a27a0f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -308,36 +308,18 @@
*/
List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg);
- @UnsupportedAppUsage
- int getCallState();
+ @UnsupportedAppUsage
+ int getCallState();
/**
* Returns the call state for a slot.
*/
int getCallStateForSlot(int slotIndex);
- /**
- * Returns a constant indicating the type of activity on a data connection
- * (cellular).
- *
- * @see #DATA_ACTIVITY_NONE
- * @see #DATA_ACTIVITY_IN
- * @see #DATA_ACTIVITY_OUT
- * @see #DATA_ACTIVITY_INOUT
- * @see #DATA_ACTIVITY_DORMANT
- */
- int getDataActivity(int subId);
-
- /**
- * Returns a constant indicating the current data connection state
- * (cellular).
- *
- * @see #DATA_DISCONNECTED
- * @see #DATA_CONNECTING
- * @see #DATA_CONNECTED
- * @see #DATA_SUSPENDED
- */
- int getDataState(int subId);
+ @UnsupportedAppUsage
+ int getDataActivity();
+ @UnsupportedAppUsage
+ int getDataState();
/**
* Returns the current active phone type as integer.
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index f2f3c2d..dc026d4 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -68,8 +68,7 @@
void notifyOtaspChanged(in int subId, in int otaspMode);
@UnsupportedAppUsage
void notifyCellInfo(in List<CellInfo> cellInfo);
- void notifyPhysicalChannelConfiguration(in List<PhysicalChannelConfig> configs);
- void notifyPhysicalChannelConfigurationForSubscriber(in int subId,
+ void notifyPhysicalChannelConfigurationForSubscriber(in int phoneId, in int subId,
in List<PhysicalChannelConfig> configs);
void notifyPreciseCallState(int phoneId, int subId, int ringingCallState,
int foregroundCallState, int backgroundCallState);
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/very_large_photo.jpg b/tests/HwAccelerationTest/res/drawable-nodpi/very_large_photo.jpg
index 7f047b1..6e1a866 100644
--- a/tests/HwAccelerationTest/res/drawable-nodpi/very_large_photo.jpg
+++ b/tests/HwAccelerationTest/res/drawable-nodpi/very_large_photo.jpg
Binary files differ
diff --git a/tests/UiBench/res/drawable-nodpi/frantic.jpg b/tests/UiBench/res/drawable-nodpi/frantic.jpg
index 4c62333..856b419 100644
--- a/tests/UiBench/res/drawable-nodpi/frantic.jpg
+++ b/tests/UiBench/res/drawable-nodpi/frantic.jpg
Binary files differ
diff --git a/tests/WindowlessWmTest/Android.bp b/tests/WindowlessWmTest/Android.bp
new file mode 100644
index 0000000..2ace3f3
--- /dev/null
+++ b/tests/WindowlessWmTest/Android.bp
@@ -0,0 +1,22 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test {
+ name: "WindowlessWmTest",
+ srcs: ["**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/WindowlessWmTest/AndroidManifest.xml b/tests/WindowlessWmTest/AndroidManifest.xml
new file mode 100644
index 0000000..babfd76
--- /dev/null
+++ b/tests/WindowlessWmTest/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.viewembed">
+
+ <application>
+ <activity android:name="WindowlessWmTest" android:label="View Embedding Test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+
+</manifest>
diff --git a/tests/WindowlessWmTest/src/com/android/test/viewembed/WindowlessWmTest.java b/tests/WindowlessWmTest/src/com/android/test/viewembed/WindowlessWmTest.java
new file mode 100644
index 0000000..5a146da
--- /dev/null
+++ b/tests/WindowlessWmTest/src/com/android/test/viewembed/WindowlessWmTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.viewembed;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.view.Gravity;
+import android.view.WindowlessViewRoot;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+
+public class WindowlessWmTest extends Activity implements SurfaceHolder.Callback{
+ SurfaceView mView;
+ WindowlessViewRoot mVr;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ FrameLayout content = new FrameLayout(this);
+ super.onCreate(savedInstanceState);
+ mView = new SurfaceView(this);
+ content.addView(mView, new FrameLayout.LayoutParams(
+ 500, 500, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
+ setContentView(content);
+
+ mView.setZOrderOnTop(true);
+ mView.getHolder().addCallback(this);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mVr = new WindowlessViewRoot(this, this.getDisplay(),
+ mView.getSurfaceControl());
+ Button v = new Button(this);
+ v.setBackgroundColor(Color.BLUE);
+ v.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ v.setBackgroundColor(Color.RED);
+ }
+ });
+ WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(500, 500, WindowManager.LayoutParams.TYPE_APPLICATION,
+ 0, PixelFormat.OPAQUE);
+ mVr.addView(v, lp);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(Color.GREEN);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 231a013..2ce6b13 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -159,6 +159,7 @@
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
+import android.os.BadParcelableException;
import android.os.Binder;
import android.os.Bundle;
import android.os.ConditionVariable;
@@ -204,7 +205,6 @@
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkStatsFactory;
import com.android.testutils.HandlerUtilsKt;
import com.android.testutils.ThrowingConsumer;
@@ -3128,24 +3128,18 @@
@Test
public void testInvalidNetworkSpecifier() {
- try {
+ assertThrows(IllegalArgumentException.class, () -> {
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.setNetworkSpecifier(new MatchAllNetworkSpecifier());
- fail("NetworkRequest builder with MatchAllNetworkSpecifier");
- } catch (IllegalArgumentException expected) {
- // expected
- }
+ });
- try {
+ assertThrows(IllegalArgumentException.class, () -> {
NetworkCapabilities networkCapabilities = new NetworkCapabilities();
networkCapabilities.addTransportType(TRANSPORT_WIFI)
.setNetworkSpecifier(new MatchAllNetworkSpecifier());
mService.requestNetwork(networkCapabilities, null, 0, null,
ConnectivityManager.TYPE_WIFI);
- fail("ConnectivityService requestNetwork with MatchAllNetworkSpecifier");
- } catch (IllegalArgumentException expected) {
- // expected
- }
+ });
class NonParcelableSpecifier extends NetworkSpecifier {
public boolean satisfiedBy(NetworkSpecifier other) { return false; }
@@ -3154,24 +3148,22 @@
@Override public int describeContents() { return 0; }
@Override public void writeToParcel(Parcel p, int flags) {}
}
- NetworkRequest.Builder builder;
- builder = new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
- try {
+ final NetworkRequest.Builder builder =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
+ assertThrows(ClassCastException.class, () -> {
builder.setNetworkSpecifier(new NonParcelableSpecifier());
Parcel parcelW = Parcel.obtain();
builder.build().writeToParcel(parcelW, 0);
- fail("Parceling a non-parcelable specifier did not throw an exception");
- } catch (Exception e) {
- // expected
- }
+ });
- builder = new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
- builder.setNetworkSpecifier(new ParcelableSpecifier());
- NetworkRequest nr = builder.build();
+ final NetworkRequest nr =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET)
+ .setNetworkSpecifier(new ParcelableSpecifier())
+ .build();
assertNotNull(nr);
- try {
+ assertThrows(BadParcelableException.class, () -> {
Parcel parcelW = Parcel.obtain();
nr.writeToParcel(parcelW, 0);
byte[] bytes = parcelW.marshall();
@@ -3181,10 +3173,7 @@
parcelR.unmarshall(bytes, 0, bytes.length);
parcelR.setDataPosition(0);
NetworkRequest rereadNr = NetworkRequest.CREATOR.createFromParcel(parcelR);
- fail("Unparceling a non-framework NetworkSpecifier did not throw an exception");
- } catch (Exception e) {
- // expected
- }
+ });
}
@Test
@@ -3213,12 +3202,9 @@
NetworkRequest networkRequest = newWifiRequestBuilder().setNetworkSpecifier(
networkSpecifier).build();
TestNetworkCallback networkCallback = new TestNetworkCallback();
- try {
+ assertThrows(SecurityException.class, () -> {
mCm.requestNetwork(networkRequest, networkCallback);
- fail("Network request with spoofed UID did not throw a SecurityException");
- } catch (SecurityException e) {
- // expected
- }
+ });
}
@Test
@@ -3230,36 +3216,20 @@
.build();
// Registering a NetworkCallback with signal strength but w/o NETWORK_SIGNAL_STRENGTH_WAKEUP
// permission should get SecurityException.
- try {
- mCm.registerNetworkCallback(r, new NetworkCallback());
- fail("Expected SecurityException filing a callback with signal strength");
- } catch (SecurityException expected) {
- // expected
- }
+ assertThrows(SecurityException.class, () ->
+ mCm.registerNetworkCallback(r, new NetworkCallback()));
- try {
- mCm.registerNetworkCallback(r, PendingIntent.getService(
- mServiceContext, 0, new Intent(), 0));
- fail("Expected SecurityException filing a callback with signal strength");
- } catch (SecurityException expected) {
- // expected
- }
+ assertThrows(SecurityException.class, () ->
+ mCm.registerNetworkCallback(r, PendingIntent.getService(
+ mServiceContext, 0, new Intent(), 0)));
// Requesting a Network with signal strength should get IllegalArgumentException.
- try {
- mCm.requestNetwork(r, new NetworkCallback());
- fail("Expected IllegalArgumentException filing a request with signal strength");
- } catch (IllegalArgumentException expected) {
- // expected
- }
+ assertThrows(IllegalArgumentException.class, () ->
+ mCm.requestNetwork(r, new NetworkCallback()));
- try {
- mCm.requestNetwork(r, PendingIntent.getService(
- mServiceContext, 0, new Intent(), 0));
- fail("Expected IllegalArgumentException filing a request with signal strength");
- } catch (IllegalArgumentException expected) {
- // expected
- }
+ assertThrows(IllegalArgumentException.class, () ->
+ mCm.requestNetwork(r, PendingIntent.getService(
+ mServiceContext, 0, new Intent(), 0)));
}
@Test
@@ -4730,25 +4700,20 @@
}
// Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
- try {
- mCm.requestNetwork(networkRequest, new NetworkCallback());
- fail("Registering " + MAX_REQUESTS + " network requests did not throw exception");
- } catch (TooManyRequestsException expected) {}
- try {
- mCm.registerNetworkCallback(networkRequest, new NetworkCallback());
- fail("Registering " + MAX_REQUESTS + " network callbacks did not throw exception");
- } catch (TooManyRequestsException expected) {}
- try {
- mCm.requestNetwork(networkRequest,
- PendingIntent.getBroadcast(mContext, 0, new Intent("c"), 0));
- fail("Registering " + MAX_REQUESTS + " PendingIntent requests did not throw exception");
- } catch (TooManyRequestsException expected) {}
- try {
- mCm.registerNetworkCallback(networkRequest,
- PendingIntent.getBroadcast(mContext, 0, new Intent("d"), 0));
- fail("Registering " + MAX_REQUESTS
- + " PendingIntent callbacks did not throw exception");
- } catch (TooManyRequestsException expected) {}
+ assertThrows(TooManyRequestsException.class, () ->
+ mCm.requestNetwork(networkRequest, new NetworkCallback())
+ );
+ assertThrows(TooManyRequestsException.class, () ->
+ mCm.registerNetworkCallback(networkRequest, new NetworkCallback())
+ );
+ assertThrows(TooManyRequestsException.class, () ->
+ mCm.requestNetwork(networkRequest,
+ PendingIntent.getBroadcast(mContext, 0, new Intent("c"), 0))
+ );
+ assertThrows(TooManyRequestsException.class, () ->
+ mCm.registerNetworkCallback(networkRequest,
+ PendingIntent.getBroadcast(mContext, 0, new Intent("d"), 0))
+ );
for (Object o : registered) {
if (o instanceof NetworkCallback) {
@@ -4916,11 +4881,8 @@
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
// Default network switch should update ifaces.
@@ -4929,65 +4891,47 @@
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyWifi),
- any(NetworkState[].class),
- eq(WIFI_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyWifi), any(NetworkState[].class), eq(WIFI_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
// Disconnect should update ifaces.
mWiFiNetworkAgent.disconnect();
waitForIdle();
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class),
+ eq(MOBILE_IFNAME), eq(new VpnInfo[0]));
reset(mStatsService);
// Metered change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
// Captive portal change shouldn't update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
waitForIdle();
verify(mStatsService, never())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
// Roaming change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
waitForIdle();
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index 7329474..a21f509 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -79,8 +79,7 @@
// related to networkStatsFactory is compiled to a minimal native library and loaded here.
System.loadLibrary("networkstatsfactorytestjni");
mFactory = new NetworkStatsFactory(mTestProc, false);
- NetworkStatsFactory.updateVpnInfos(new VpnInfo[0]);
- NetworkStatsFactory.clearStackedIfaces();
+ mFactory.updateVpnInfos(new VpnInfo[0]);
}
@After
@@ -107,7 +106,7 @@
@Test
public void vpnRewriteTrafficThroughItself() throws Exception {
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -136,8 +135,8 @@
@Test
public void vpnWithClat() throws Exception {
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
- NetworkStatsFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE);
+ mFactory.updateVpnInfos(vpnInfos);
+ mFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -169,7 +168,7 @@
@Test
public void vpnWithOneUnderlyingIface() throws Exception {
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -193,7 +192,7 @@
public void vpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -221,7 +220,7 @@
public void vpnWithOneUnderlyingIface_withCompression() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -244,7 +243,7 @@
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is duplicating traffic across both WiFi and Cell.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -270,7 +269,7 @@
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -297,7 +296,7 @@
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface:
// 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
@@ -319,7 +318,7 @@
// WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
// but has declared only WiFi (TEST_IFACE) in its underlying network set.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -383,7 +382,7 @@
@Test
public void testDoubleClatAccountingSimple() throws Exception {
- NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
+ mFactory.noteStackedIface("v4-wlan0", "wlan0");
// xt_qtaguid_with_clat_simple is a synthetic file that simulates
// - 213 received 464xlat packets of size 200 bytes
@@ -398,7 +397,7 @@
@Test
public void testDoubleClatAccounting() throws Exception {
- NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
+ mFactory.noteStackedIface("v4-wlan0", "wlan0");
NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat);
assertEquals(42, stats.size());
@@ -419,8 +418,6 @@
assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L);
assertNoStatsEntry(stats, "wlan0", 1029, SET_DEFAULT, 0x0);
-
- NetworkStatsFactory.clearStackedIfaces();
}
@Test
@@ -435,7 +432,7 @@
long rootRxBytesAfter = 1398634L;
assertEquals("UID 0 traffic should be ~0", 4623, rootRxBytesAfter - rootRxBytesBefore);
- NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
+ mFactory.noteStackedIface("v4-wlan0", "wlan0");
NetworkStats stats;
// Stats snapshot before the download
@@ -447,8 +444,6 @@
stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after);
assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 0L);
-
- NetworkStatsFactory.clearStackedIfaces();
}
/**
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index adaef40..1d29a82 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -37,6 +37,7 @@
import static android.net.NetworkStats.SET_FOREGROUND;
import static android.net.NetworkStats.STATS_PER_IFACE;
import static android.net.NetworkStats.STATS_PER_UID;
+import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
@@ -95,6 +96,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
@@ -154,6 +156,7 @@
private File mStatsDir;
private @Mock INetworkManagementService mNetManager;
+ private @Mock NetworkStatsFactory mStatsFactory;
private @Mock NetworkStatsSettings mSettings;
private @Mock IBinder mBinder;
private @Mock AlarmManager mAlarmManager;
@@ -189,8 +192,8 @@
mService = new NetworkStatsService(
mServiceContext, mNetManager, mAlarmManager, wakeLock, mClock,
- TelephonyManager.getDefault(), mSettings, new NetworkStatsObservers(),
- mStatsDir, getBaseDir(mStatsDir));
+ TelephonyManager.getDefault(), mSettings, mStatsFactory,
+ new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir));
mHandlerThread = new HandlerThread("HandlerThread");
mHandlerThread.start();
Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService);
@@ -205,7 +208,7 @@
mService.systemReady();
// Verify that system ready fetches realtime stats
- verify(mNetManager).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
+ verify(mStatsFactory).readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
mSession = mService.openSession();
assertNotNull("openSession() failed", mSession);
@@ -240,7 +243,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -283,7 +286,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -356,7 +359,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// modify some number on wifi, and trigger poll event
incrementCurrentTime(2 * HOUR_IN_MILLIS);
@@ -396,7 +399,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
// create some traffic on first network
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -430,7 +433,7 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
.addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
forcePollAndWaitForIdle();
@@ -469,7 +472,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -526,7 +529,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
// create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -552,7 +555,7 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
forcePollAndWaitForIdle();
@@ -581,7 +584,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// create some traffic for two apps
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -638,7 +641,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
NetworkStats.Entry entry1 = new NetworkStats.Entry(
TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L);
@@ -681,7 +684,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
NetworkStats.Entry uidStats = new NetworkStats.Entry(
TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
@@ -693,10 +696,13 @@
"otherif", UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
final String[] ifaceFilter = new String[] { TEST_IFACE };
+ final String[] augmentedIfaceFilter = new String[] { stackedIface, TEST_IFACE };
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
- when(mNetManager.getNetworkStatsUidDetail(eq(UID_ALL), any()))
+ when(mStatsFactory.augmentWithStackedInterfaces(eq(ifaceFilter)))
+ .thenReturn(augmentedIfaceFilter);
+ when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL)))
.thenReturn(new NetworkStats(getElapsedRealtime(), 1)
.addValues(uidStats));
when(mNetManager.getNetworkStatsTethering(STATS_PER_UID))
@@ -706,15 +712,17 @@
NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
- // mNetManager#getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL) has following invocations:
+ // mStatsFactory#readNetworkStatsDetail() has the following invocations:
// 1) NetworkStatsService#systemReady from #setUp.
// 2) mService#forceUpdateIfaces in the test above.
//
// Additionally, we should have one call from the above call to mService#getDetailedUidStats
- // with the augmented ifaceFilter
- verify(mNetManager, times(2)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
- verify(mNetManager, times(1)).getNetworkStatsUidDetail(
- eq(UID_ALL), eq(NetworkStatsFactory.augmentWithStackedInterfaces(ifaceFilter)));
+ // with the augmented ifaceFilter.
+ verify(mStatsFactory, times(2)).readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
+ verify(mStatsFactory, times(1)).readNetworkStatsDetail(
+ eq(UID_ALL),
+ eq(augmentedIfaceFilter),
+ eq(TAG_ALL));
assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE));
assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface));
assertEquals(2, stats.size());
@@ -730,7 +738,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -786,7 +794,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -825,7 +833,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
// Create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -862,7 +870,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
// create some tethering traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -902,7 +910,7 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -1062,11 +1070,11 @@
}
private void expectNetworkStatsSummaryDev(NetworkStats summary) throws Exception {
- when(mNetManager.getNetworkStatsSummaryDev()).thenReturn(summary);
+ when(mStatsFactory.readNetworkStatsSummaryDev()).thenReturn(summary);
}
private void expectNetworkStatsSummaryXt(NetworkStats summary) throws Exception {
- when(mNetManager.getNetworkStatsSummaryXt()).thenReturn(summary);
+ when(mStatsFactory.readNetworkStatsSummaryXt()).thenReturn(summary);
}
private void expectNetworkStatsTethering(int how, NetworkStats stats)
@@ -1080,7 +1088,8 @@
private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats)
throws Exception {
- when(mNetManager.getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL)).thenReturn(detail);
+ when(mStatsFactory.readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL))
+ .thenReturn(detail);
// also include tethering details, since they are folded into UID
when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats);
diff --git a/tools/lock_agent/agent.cpp b/tools/lock_agent/agent.cpp
index c639427..40293b6 100644
--- a/tools/lock_agent/agent.cpp
+++ b/tools/lock_agent/agent.cpp
@@ -85,118 +85,143 @@
using namespace dex;
using namespace lir;
-bool transform(std::shared_ptr<ir::DexFile> dexIr) {
- bool modified = false;
+class Transformer {
+public:
+ explicit Transformer(std::shared_ptr<ir::DexFile> dexIr) : dexIr_(dexIr) {}
- std::unique_ptr<ir::Builder> builder;
+ bool transform() {
+ bool classModified = false;
- for (auto& method : dexIr->encoded_methods) {
- // Do not look into abstract/bridge/native/synthetic methods.
- if ((method->access_flags & (kAccAbstract | kAccBridge | kAccNative | kAccSynthetic))
- != 0) {
- continue;
+ std::unique_ptr<ir::Builder> builder;
+
+ for (auto& method : dexIr_->encoded_methods) {
+ // Do not look into abstract/bridge/native/synthetic methods.
+ if ((method->access_flags & (kAccAbstract | kAccBridge | kAccNative | kAccSynthetic))
+ != 0) {
+ continue;
+ }
+
+ struct HookVisitor: public Visitor {
+ HookVisitor(Transformer* transformer, CodeIr* c_ir)
+ : transformer(transformer), cIr(c_ir) {
+ }
+
+ bool Visit(Bytecode* bytecode) override {
+ if (bytecode->opcode == OP_MONITOR_ENTER) {
+ insertHook(bytecode, true,
+ reinterpret_cast<VReg*>(bytecode->operands[0])->reg);
+ return true;
+ }
+ if (bytecode->opcode == OP_MONITOR_EXIT) {
+ insertHook(bytecode, false,
+ reinterpret_cast<VReg*>(bytecode->operands[0])->reg);
+ return true;
+ }
+ return false;
+ }
+
+ void insertHook(lir::Instruction* before, bool pre, u4 reg) {
+ transformer->preparePrePost();
+ transformer->addCall(cIr, before, OP_INVOKE_STATIC_RANGE,
+ transformer->hookType_, pre ? "preLock" : "postLock",
+ transformer->voidType_, transformer->objectType_, reg);
+ myModified = true;
+ }
+
+ Transformer* transformer;
+ CodeIr* cIr;
+ bool myModified = false;
+ };
+
+ CodeIr c(method.get(), dexIr_);
+ bool methodModified = false;
+
+ HookVisitor visitor(this, &c);
+ for (auto it = c.instructions.begin(); it != c.instructions.end(); ++it) {
+ lir::Instruction* fi = *it;
+ fi->Accept(&visitor);
+ }
+ methodModified |= visitor.myModified;
+
+ if (methodModified) {
+ classModified = true;
+ c.Assemble();
+ }
}
- struct HookVisitor: public Visitor {
- HookVisitor(std::unique_ptr<ir::Builder>* b, std::shared_ptr<ir::DexFile> d_ir,
- CodeIr* c_ir) :
- b(b), dIr(d_ir), cIr(c_ir) {
- }
+ return classModified;
+ }
- bool Visit(Bytecode* bytecode) override {
- if (bytecode->opcode == OP_MONITOR_ENTER) {
- prepare();
- addCall(bytecode, OP_INVOKE_STATIC_RANGE, hookType, "preLock", voidType,
- objectType, reinterpret_cast<VReg*>(bytecode->operands[0])->reg);
- myModified = true;
- return true;
- }
- if (bytecode->opcode == OP_MONITOR_EXIT) {
- prepare();
- addCall(bytecode, OP_INVOKE_STATIC_RANGE, hookType, "postLock", voidType,
- objectType, reinterpret_cast<VReg*>(bytecode->operands[0])->reg);
- myModified = true;
- return true;
- }
- return false;
- }
+private:
+ void preparePrePost() {
+ // Insert "void LockHook.(pre|post)(Object o)."
- void prepare() {
- if (*b == nullptr) {
- *b = std::unique_ptr<ir::Builder>(new ir::Builder(dIr));
- }
- if (voidType == nullptr) {
- voidType = (*b)->GetType("V");
- hookType = (*b)->GetType("Lcom/android/lock_checker/LockHook;");
- objectType = (*b)->GetType("Ljava/lang/Object;");
- }
- }
+ prepareBuilder();
- void addInst(lir::Instruction* instructionAfter, Opcode opcode,
- const std::list<Operand*>& operands) {
- auto instruction = cIr->Alloc<Bytecode>();
-
- instruction->opcode = opcode;
-
- for (auto it = operands.begin(); it != operands.end(); it++) {
- instruction->operands.push_back(*it);
- }
-
- cIr->instructions.InsertBefore(instructionAfter, instruction);
- }
-
- void addCall(lir::Instruction* instructionAfter, Opcode opcode, ir::Type* type,
- const char* methodName, ir::Type* returnType,
- const std::vector<ir::Type*>& types, const std::list<int>& regs) {
- auto proto = (*b)->GetProto(returnType, (*b)->GetTypeList(types));
- auto method = (*b)->GetMethodDecl((*b)->GetAsciiString(methodName), proto, type);
-
- VRegList* paramRegs = cIr->Alloc<VRegList>();
- for (auto it = regs.begin(); it != regs.end(); it++) {
- paramRegs->registers.push_back(*it);
- }
-
- addInst(instructionAfter, opcode,
- { paramRegs, cIr->Alloc<Method>(method, method->orig_index) });
- }
-
- void addCall(lir::Instruction* instructionAfter, Opcode opcode, ir::Type* type,
- const char* methodName, ir::Type* returnType, ir::Type* paramType,
- u4 paramVReg) {
- auto proto = (*b)->GetProto(returnType, (*b)->GetTypeList( { paramType }));
- auto method = (*b)->GetMethodDecl((*b)->GetAsciiString(methodName), proto, type);
-
- VRegRange* args = cIr->Alloc<VRegRange>(paramVReg, 1);
-
- addInst(instructionAfter, opcode,
- { args, cIr->Alloc<Method>(method, method->orig_index) });
- }
-
- std::unique_ptr<ir::Builder>* b;
- std::shared_ptr<ir::DexFile> dIr;
- CodeIr* cIr;
- ir::Type* voidType = nullptr;
- ir::Type* hookType = nullptr;
- ir::Type* objectType = nullptr;
- bool myModified = false;
- };
-
- CodeIr c(method.get(), dexIr);
- HookVisitor visitor(&builder, dexIr, &c);
-
- for (auto it = c.instructions.begin(); it != c.instructions.end(); ++it) {
- lir::Instruction* fi = *it;
- fi->Accept(&visitor);
+ if (voidType_ == nullptr) {
+ voidType_ = builder_->GetType("V");
}
-
- if (visitor.myModified) {
- modified = true;
- c.Assemble();
+ if (hookType_ == nullptr) {
+ hookType_ = builder_->GetType("Lcom/android/lock_checker/LockHook;");
+ }
+ if (objectType_ == nullptr) {
+ objectType_ = builder_->GetType("Ljava/lang/Object;");
}
}
- return modified;
-}
+ void prepareBuilder() {
+ if (builder_ == nullptr) {
+ builder_ = std::unique_ptr<ir::Builder>(new ir::Builder(dexIr_));
+ }
+ }
+
+ static void addInst(CodeIr* cIr, lir::Instruction* instructionAfter, Opcode opcode,
+ const std::list<Operand*>& operands) {
+ auto instruction = cIr->Alloc<Bytecode>();
+
+ instruction->opcode = opcode;
+
+ for (auto it = operands.begin(); it != operands.end(); it++) {
+ instruction->operands.push_back(*it);
+ }
+
+ cIr->instructions.InsertBefore(instructionAfter, instruction);
+ }
+
+ void addCall(CodeIr* cIr, lir::Instruction* instructionAfter, Opcode opcode, ir::Type* type,
+ const char* methodName, ir::Type* returnType,
+ const std::vector<ir::Type*>& types, const std::list<int>& regs) {
+ auto proto = builder_->GetProto(returnType, builder_->GetTypeList(types));
+ auto method = builder_->GetMethodDecl(builder_->GetAsciiString(methodName), proto, type);
+
+ VRegList* paramRegs = cIr->Alloc<VRegList>();
+ for (auto it = regs.begin(); it != regs.end(); it++) {
+ paramRegs->registers.push_back(*it);
+ }
+
+ addInst(cIr, instructionAfter, opcode,
+ { paramRegs, cIr->Alloc<Method>(method, method->orig_index) });
+ }
+
+ void addCall(CodeIr* cIr, lir::Instruction* instructionAfter, Opcode opcode, ir::Type* type,
+ const char* methodName, ir::Type* returnType, ir::Type* paramType,
+ u4 paramVReg) {
+ auto proto = builder_->GetProto(returnType, builder_->GetTypeList( { paramType }));
+ auto method = builder_->GetMethodDecl(builder_->GetAsciiString(methodName), proto, type);
+
+ VRegRange* args = cIr->Alloc<VRegRange>(paramVReg, 1);
+
+ addInst(cIr, instructionAfter, opcode,
+ { args, cIr->Alloc<Method>(method, method->orig_index) });
+ }
+
+ std::shared_ptr<ir::DexFile> dexIr_;
+ std::unique_ptr<ir::Builder> builder_;
+
+ ir::Type* voidType_ = nullptr;
+ ir::Type* hookType_ = nullptr;
+ ir::Type* objectType_ = nullptr;
+};
std::pair<dex::u1*, size_t> maybeTransform(const char* name, size_t classDataLen,
const unsigned char* classData, dex::Writer::Allocator* allocator) {
@@ -209,8 +234,11 @@
reader.CreateClassIr(index);
std::shared_ptr<ir::DexFile> ir = reader.GetIr();
- if (!transform(ir)) {
- return std::make_pair(nullptr, 0);
+ {
+ Transformer transformer(ir);
+ if (!transformer.transform()) {
+ return std::make_pair(nullptr, 0);
+ }
}
size_t new_size;
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
index daea601..0511f24 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
@@ -31,11 +31,8 @@
import java.util.Objects;
/**
- * Network specifier object used to request a Wi-Fi Aware network. Apps do not create these objects
- * directly but obtain them using
- * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} or
- * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or their secure (Passphrase)
- * versions.
+ * Network specifier object used to request a Wi-Fi Aware network. Apps should use the
+ * {@link WifiAwareNetworkSpecifier.Builder} class to create an instance.
*/
public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
/**