Merge "Disable historical appops"
diff --git a/Android.bp b/Android.bp
index 1902a5f..490de6e 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",
@@ -841,6 +842,7 @@
java_library {
name: "framework-annotation-proc",
defaults: ["framework-defaults"],
+ installable: false,
// Use UsedByApps annotation processor
plugins: ["unsupportedappusage-annotation-processor"],
}
@@ -1879,3 +1881,13 @@
srcs: [":framework-defaults"],
output: "framework-aidl-mappings.txt",
}
+
+genrule {
+ name: "framework-annotation-proc-index",
+ srcs: [":framework-annotation-proc"],
+ cmd: "unzip -qp $(in) unsupportedappusage/unsupportedappusage_index.csv > $(out)",
+ out: ["unsupportedappusage_index.csv"],
+ dist: {
+ targets: ["droidcore"],
+ },
+}
diff --git a/api/test-current.txt b/api/test-current.txt
index bf2730a..14c70e3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -795,6 +795,7 @@
}
public final class RollbackManager {
+ method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void blockRollbackManager(long);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) public void commitRollback(int, @NonNull java.util.List<android.content.pm.VersionedPackage>, @NonNull android.content.IntentSender);
method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks();
@@ -2301,6 +2302,7 @@
field public static final String NAMESPACE_PRIVACY = "privacy";
field public static final String NAMESPACE_ROLLBACK = "rollback";
field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
+ field public static final String NAMESPACE_WINDOW_MANAGER = "android:window_manager";
}
public static interface DeviceConfig.OnPropertiesChangedListener {
@@ -2317,6 +2319,10 @@
method @Nullable public String getString(@NonNull String, @Nullable String);
}
+ public static interface DeviceConfig.WindowManager {
+ field public static final String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp";
+ }
+
public final class MediaStore {
method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static void deleteContributedMedia(android.content.Context, String, android.os.UserHandle) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException;
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/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 10f25ea..c30a6f4 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -581,6 +581,7 @@
Landroid/service/dreams/IDreamManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/dreams/IDreamManager;
Landroid/service/dreams/IDreamManager;->getDreamComponents()[Landroid/content/ComponentName;
Landroid/service/euicc/IEuiccService$Stub;-><init>()V
+Landroid/service/media/IMediaBrowserServiceCallbacks$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/media/IMediaBrowserServiceCallbacks;
Landroid/service/notification/INotificationListener$Stub;-><init>()V
Landroid/service/persistentdata/IPersistentDataBlockService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/persistentdata/IPersistentDataBlockService;
Landroid/service/vr/IVrManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/vr/IVrManager;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7a614ce..525bc44b 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1050,6 +1050,7 @@
}
}
+ @UnsupportedAppUsage
@Override
public boolean setInstantAppCookie(@NonNull byte[] cookie) {
try {
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/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 60fb6fb..a812c32 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -22,6 +22,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -83,6 +84,7 @@
*
* @hide
*/
+ @UnsupportedAppUsage
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ACTIVE_DEVICE_CHANGED =
"android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
@@ -303,6 +305,7 @@
* @return false on immediate error, true otherwise
* @hide
*/
+ @UnsupportedAppUsage
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
final IBluetoothHearingAid service = getService();
@@ -331,6 +334,7 @@
* is not active, it will be null on that position. Returns empty list on error.
* @hide
*/
+ @UnsupportedAppUsage
@RequiresPermission(Manifest.permission.BLUETOOTH)
public List<BluetoothDevice> getActiveDevices() {
if (VDBG) log("getActiveDevices()");
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 1838bab..f39fc66 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
import android.annotation.UserIdInt;
import android.os.Parcel;
import android.os.Parcelable;
@@ -169,6 +170,7 @@
* The state of this OverlayInfo as defined by the STATE_* constants in this class.
* @hide
*/
+ @UnsupportedAppUsage
public final @State int state;
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index a2994af..cf858f5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -8397,6 +8397,7 @@
PackageInfo pi = generatePackageInfo(p, EmptyArray.INT, flags, 0, 0,
Collections.emptySet(), state);
pi.applicationInfo.sourceDir = apexFile.getPath();
+ pi.applicationInfo.publicSourceDir = apexFile.getPath();
if (apexInfo.isFactory) {
pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
} else {
diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl
index 1b84f29..8c2a65f 100644
--- a/core/java/android/content/rollback/IRollbackManager.aidl
+++ b/core/java/android/content/rollback/IRollbackManager.aidl
@@ -24,7 +24,7 @@
interface IRollbackManager {
ParceledListSlice getAvailableRollbacks();
- ParceledListSlice getRecentlyExecutedRollbacks();
+ ParceledListSlice getRecentlyCommittedRollbacks();
void commitRollback(int rollbackId, in ParceledListSlice causePackages,
String callerPackageName, in IntentSender statusReceiver);
@@ -51,4 +51,7 @@
// Used by the staging manager to notify the RollbackManager of the apk
// session for a staged session.
void notifyStagedApkSession(int originalSessionId, int apkSessionId);
+
+ // For test purposes only.
+ void blockRollbackManager(long millis);
}
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 9a10a0c..73b8a48 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -114,7 +114,7 @@
})
public @NonNull List<RollbackInfo> getRecentlyCommittedRollbacks() {
try {
- return mBinder.getRecentlyExecutedRollbacks().getList();
+ return mBinder.getRecentlyCommittedRollbacks().getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -250,4 +250,25 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Block the RollbackManager for a specified amount of time.
+ * This API is meant to facilitate testing of race conditions in
+ * RollbackManager. Blocks RollbackManager from processing anything for
+ * the given number of milliseconds.
+ *
+ * @param millis number of milliseconds to block the RollbackManager for
+ * @throws SecurityException if the caller does not have appropriate permissions.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
+ @TestApi
+ public void blockRollbackManager(long millis) {
+ try {
+ mBinder.blockRollbackManager(millis);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
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/UserHandle.java b/core/java/android/os/UserHandle.java
index b121234..d70ba99 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -68,6 +68,7 @@
public static final @NonNull UserHandle CURRENT_OR_SELF = new UserHandle(USER_CURRENT_OR_SELF);
/** @hide An undefined user id */
+ @UnsupportedAppUsage
public static final @UserIdInt int USER_NULL = -10000;
/**
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/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 01b6758..48faf11 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -293,6 +293,15 @@
public static final String NAMESPACE_SETTINGS_UI = "settings_ui";
/**
+ * Namespace for window manager related features. The names to access the properties in this
+ * namespace should be defined in {@link WindowManager}.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String NAMESPACE_WINDOW_MANAGER = "android:window_manager";
+
+ /**
* List of namespaces which can be read without READ_DEVICE_CONFIG permission
*
* @hide
@@ -309,6 +318,23 @@
@TestApi
public static final String NAMESPACE_PRIVACY = "privacy";
+ /**
+ * Interface for accessing keys belonging to {@link #NAMESPACE_WINDOW_MANAGER}.
+ * @hide
+ */
+ @TestApi
+ public interface WindowManager {
+
+ /**
+ * Key for accessing the system gesture exclusion limit (an integer in dp).
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
+ * @hide
+ */
+ @TestApi
+ String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp";
+ }
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
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/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/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 882e6fd..2e9d881 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -184,7 +184,7 @@
boolean mIsTouchExplorationEnabled;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768939)
+ @UnsupportedAppUsage(trackingBug = 123768939L)
boolean mIsHighTextContrastEnabled;
AccessibilityPolicy mAccessibilityPolicy;
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/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 564d972..86cec5e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -206,13 +206,13 @@
*
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public ApplicationInfo mApplication;
/**
* The resource ID of the layout file. (Added to the parcel)
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
private final int mLayoutId;
/**
@@ -224,13 +224,13 @@
* An array of actions to perform on the view tree once it has been
* inflated
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
private ArrayList<Action> mActions;
/**
* Maps bitmaps to unique indicies to avoid Bitmap duplication.
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
private BitmapCache mBitmapCache;
/**
@@ -252,7 +252,7 @@
* RemoteViews.
*/
private RemoteViews mLandscape = null;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
private RemoteViews mPortrait = null;
@ApplyFlags
@@ -430,7 +430,7 @@
// Do nothing
}
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public int mergeBehavior() {
return MERGE_REPLACE;
}
@@ -466,7 +466,7 @@
// Nothing to visit by default
}
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
int viewId;
}
@@ -499,7 +499,7 @@
*
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public void mergeRemoteViews(RemoteViews newRv) {
if (newRv == null) return;
// We first copy the new RemoteViews, as the process of merging modifies the way the actions
@@ -690,7 +690,7 @@
return SET_PENDING_INTENT_TEMPLATE_TAG;
}
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
PendingIntent pendingIntentTemplate;
}
@@ -1138,7 +1138,7 @@
private static class BitmapCache {
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
ArrayList<Bitmap> mBitmaps;
int mBitmapMemory = -1;
@@ -1190,9 +1190,9 @@
private class BitmapReflectionAction extends Action {
int bitmapId;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
Bitmap bitmap;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
String methodName;
BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
@@ -1258,10 +1258,10 @@
static final int COLOR_STATE_LIST = 15;
static final int ICON = 16;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
String methodName;
int type;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
Object value;
ReflectionAction(int viewId, String methodName, int type, Object value) {
@@ -1554,7 +1554,7 @@
* ViewGroup methods that are related to adding Views.
*/
private class ViewGroupActionAdd extends Action {
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
private RemoteViews mNestedViews;
private int mIndex;
@@ -2469,7 +2469,7 @@
* Returns an estimate of the bitmap heap memory usage for this RemoteViews.
*/
/** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public int estimateMemoryUsage() {
return mBitmapCache.getBitmapMemory();
}
@@ -2517,7 +2517,7 @@
*
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public void addView(int viewId, RemoteViews nestedView, int index) {
addAction(new ViewGroupActionAdd(viewId, nestedView, index));
}
@@ -2994,7 +2994,8 @@
* @hide
* @deprecated this appears to have no users outside of UnsupportedAppUsage?
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
+ @Deprecated
public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 7cc8128..f905ea2 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -29,7 +29,6 @@
import android.app.VoiceInteractor.PickOptionRequest;
import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.app.VoiceInteractor.Prompt;
-import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -111,7 +110,6 @@
protected AbsListView mAdapterView;
private Button mAlwaysButton;
private Button mOnceButton;
- private Button mSettingsButton;
protected View mProfileView;
private int mIconDpi;
private int mLastSelected = AbsListView.INVALID_POSITION;
@@ -146,6 +144,10 @@
/** See {@link #setRetainInOnStop}. */
private boolean mRetainInOnStop;
+ private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
+ private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+ private static final String OPEN_LINKS_COMPONENT_KEY = "app_link_state";
+
private final PackageMonitor mPackageMonitor = createPackageMonitor();
/**
@@ -196,9 +198,13 @@
// titles for layout that deals with http(s) intents
public static final int BROWSABLE_TITLE_RES =
- com.android.internal.R.string.whichGiveAccessToApplication;
- public static final int BROWSABLE_NAMED_TITLE_RES =
- com.android.internal.R.string.whichGiveAccessToApplicationNamed;
+ com.android.internal.R.string.whichOpenLinksWith;
+ public static final int BROWSABLE_HOST_TITLE_RES =
+ com.android.internal.R.string.whichOpenHostLinksWith;
+ public static final int BROWSABLE_HOST_APP_TITLE_RES =
+ com.android.internal.R.string.whichOpenHostLinksWithApp;
+ public static final int BROWSABLE_APP_TITLE_RES =
+ com.android.internal.R.string.whichOpenLinksWithApp;
public final String action;
public final int titleRes;
@@ -322,9 +328,7 @@
? false
: isHttpSchemeAndViewAction(getTargetIntent());
- // We don't want to support Always Use if browsable layout is being used,
- // as to mitigate Intent Capturing vulnerability
- mSupportsAlwaysUseOption = supportsAlwaysUseOption && !mUseLayoutForBrowsables;
+ mSupportsAlwaysUseOption = supportsAlwaysUseOption;
if (configureContentView(mIntents, initialIntents, rList)) {
return;
@@ -554,10 +558,21 @@
} else if (isHttpSchemeAndViewAction(intent)) {
// If the Intent's scheme is http(s) then we need to warn the user that
// they're giving access for the activity to open URLs from this specific host
- return named
- ? getString(ActionTitle.BROWSABLE_NAMED_TITLE_RES, intent.getData().getHost(),
- mAdapter.getFilteredItem().getDisplayLabel())
- : getString(ActionTitle.BROWSABLE_TITLE_RES, intent.getData().getHost());
+ String dialogTitle = null;
+ if (named && !mUseLayoutForBrowsables) {
+ dialogTitle = getString(ActionTitle.BROWSABLE_APP_TITLE_RES,
+ mAdapter.getFilteredItem().getDisplayLabel());
+ } else if (named && mUseLayoutForBrowsables) {
+ dialogTitle = getString(ActionTitle.BROWSABLE_HOST_APP_TITLE_RES,
+ intent.getData().getHost(),
+ mAdapter.getFilteredItem().getDisplayLabel());
+ } else if (mAdapter.areAllTargetsBrowsers()) {
+ dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
+ } else {
+ dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES,
+ intent.getData().getHost());
+ }
+ return dialogTitle;
} else {
return named
? getString(title.namedTitleRes, mAdapter.getFilteredItem().getDisplayLabel())
@@ -856,6 +871,13 @@
} else {
enabled = true;
}
+ if (mUseLayoutForBrowsables && !ri.handleAllWebDataURI) {
+ mAlwaysButton.setText(getResources()
+ .getString(R.string.activity_resolver_set_always));
+ } else {
+ mAlwaysButton.setText(getResources()
+ .getString(R.string.activity_resolver_use_always));
+ }
}
mAlwaysButton.setEnabled(enabled);
}
@@ -866,26 +888,29 @@
? mAdapter.getFilteredPosition()
: mAdapterView.getCheckedItemPosition();
boolean hasIndexBeenFiltered = !mAdapter.hasFilteredItem();
- if (id == R.id.button_app_settings) {
- showSettingsForSelected(which, hasIndexBeenFiltered);
+ ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
+ if (!ri.handleAllWebDataURI && id == R.id.button_always) {
+ showSettingsForSelected(ri);
} else {
startSelected(which, id == R.id.button_always, hasIndexBeenFiltered);
}
}
- private void showSettingsForSelected(int which, boolean hasIndexBeenFiltered) {
- ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
+ private void showSettingsForSelected(ResolveInfo ri) {
Intent intent = new Intent();
- // For browsers, we open the Default Browser page
+
+ final String packageName = ri.activityInfo.packageName;
+ Bundle showFragmentArgs = new Bundle();
+ showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, OPEN_LINKS_COMPONENT_KEY);
+ showFragmentArgs.putString("package", packageName);
+
// For regular apps, we open the Open by Default page
- if (ri.handleAllWebDataURI) {
- intent.setAction(Intent.ACTION_MANAGE_DEFAULT_APP)
- .putExtra(Intent.EXTRA_ROLE_NAME, RoleManager.ROLE_BROWSER);
- } else {
- intent.setAction(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS)
- .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- }
+ intent.setAction(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS)
+ .setData(Uri.fromParts("package", packageName, null))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
+ .putExtra(EXTRA_FRAGMENT_ARG_KEY, OPEN_LINKS_COMPONENT_KEY)
+ .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
+
startActivity(intent);
}
@@ -1332,41 +1357,15 @@
R.dimen.resolver_button_bar_spacing) + inset);
mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
- mSettingsButton = (Button) buttonLayout.findViewById(R.id.button_app_settings);
mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
- if (mUseLayoutForBrowsables) {
- resetSettingsOrOnceButtonBar();
- } else {
- resetAlwaysOrOnceButtonBar();
- }
+ resetAlwaysOrOnceButtonBar();
} else {
Log.e(TAG, "Layout unexpectedly does not have a button bar");
}
}
- private void resetSettingsOrOnceButtonBar() {
- //unsetting always button
- mAlwaysButton.setVisibility(View.GONE);
-
- // When the items load in, if an item was already selected,
- // enable the buttons
- if (mAdapterView != null
- && mAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
- mSettingsButton.setEnabled(true);
- mOnceButton.setEnabled(true);
- }
- }
-
private void resetAlwaysOrOnceButtonBar() {
- // This check needs to be made because layout with default
- // doesn't have a settings button
- if (mSettingsButton != null) {
- //unsetting always button
- mSettingsButton.setVisibility(View.GONE);
- mSettingsButton = null;
- }
-
if (useLayoutWithDefault()
&& mAdapter.getFilteredPosition() != ListView.INVALID_POSITION) {
setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
@@ -1626,6 +1625,7 @@
private DisplayResolveInfo mOtherProfile;
private ResolverListController mResolverListController;
private int mPlaceholderCount;
+ private boolean mAllTargetsAreBrowsers = false;
protected final LayoutInflater mInflater;
@@ -1701,6 +1701,14 @@
}
/**
+ * @return true if all items in the display list are defined as browsers by
+ * ResolveInfo.handleAllWebDataURI
+ */
+ public boolean areAllTargetsBrowsers() {
+ return mAllTargetsAreBrowsers;
+ }
+
+ /**
* Rebuild the list of resolvers. In some cases some parts will need some asynchronous work
* to complete.
*
@@ -1712,6 +1720,7 @@
mOtherProfile = null;
mLastChosen = null;
mLastChosenPosition = -1;
+ mAllTargetsAreBrowsers = false;
mDisplayList.clear();
if (mBaseResolveList != null) {
currentResolveList = mUnfilteredResolveList = new ArrayList<>();
@@ -1812,6 +1821,8 @@
private void processSortedList(List<ResolvedComponentInfo> sortedComponents) {
int N;
if (sortedComponents != null && (N = sortedComponents.size()) != 0) {
+ mAllTargetsAreBrowsers = mUseLayoutForBrowsables;
+
// First put the initial items at the top.
if (mInitialIntents != null) {
for (int i = 0; i < mInitialIntents.length; i++) {
@@ -1841,6 +1852,8 @@
ri.noResourceId = true;
ri.icon = 0;
}
+ mAllTargetsAreBrowsers &= ri.handleAllWebDataURI;
+
addResolveInfo(new DisplayResolveInfo(ii, ri,
ri.loadLabel(getPackageManager()), null, ii));
}
@@ -1850,6 +1863,8 @@
for (ResolvedComponentInfo rci : sortedComponents) {
final ResolveInfo ri = rci.getResolveInfoAt(0);
if (ri != null) {
+ mAllTargetsAreBrowsers &= ri.handleAllWebDataURI;
+
ResolveInfoPresentationGetter pg = makePresentationGetter(ri);
addResolveInfoWithAlternates(rci, pg.getSubLabel(), pg.getLabel());
}
@@ -2152,14 +2167,8 @@
final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
if (!useLayoutWithDefault()
&& (!hasValidSelection || mLastSelected != checkedPos)
- && (mAlwaysButton != null || mSettingsButton != null)) {
- if (mSettingsButton != null) {
- // this implies that the layout for browsables is being used
- mSettingsButton.setEnabled(true);
- } else {
- // this implies that mAlwaysButton != null
- setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
- }
+ && mAlwaysButton != null) {
+ setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
mOnceButton.setEnabled(hasValidSelection);
if (hasValidSelection) {
mAdapterView.smoothScrollToPosition(checkedPos);
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/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 64291de..d00108e 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -16,8 +16,6 @@
package com.android.internal.view.menu;
-import com.android.internal.view.menu.MenuPresenter.Callback;
-
import android.annotation.AttrRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,14 +24,14 @@
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.Build;
-import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.PopupWindow.OnDismissListener;
+import com.android.internal.view.menu.MenuPresenter.Callback;
+
/**
* Presents a menu as a small, simple popup anchored to another view.
*/
@@ -114,7 +112,7 @@
* @param forceShowIcon {@code true} to force icons to be shown, or
* {@code false} for icons to be optionally shown
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public void setForceShowIcon(boolean forceShowIcon) {
mForceShowIcon = forceShowIcon;
if (mPopup != null) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 640b8f9..b21221f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -33,12 +33,19 @@
srcs: [
"android_animation_PropertyValuesHolder.cpp",
+ "android_content_res_ApkAssets.cpp",
"android_graphics_Canvas.cpp",
"android_graphics_ColorSpace.cpp",
"android_graphics_drawable_AnimatedVectorDrawable.cpp",
"android_graphics_drawable_VectorDrawable.cpp",
"android_graphics_Picture.cpp",
"android_nio_utils.cpp",
+ "android_os_MessageQueue.cpp",
+ "android_os_SystemClock.cpp",
+ "android_os_SystemProperties.cpp",
+ "android_os_Trace.cpp",
+ "android_util_EventLog.cpp",
+ "android_util_Log.cpp",
"android_util_PathParser.cpp",
"android_view_DisplayListCanvas.cpp",
"android_view_RenderNode.cpp",
@@ -69,6 +76,9 @@
"android/graphics/fonts/FontFamily.cpp",
"android/graphics/text/LineBreaker.cpp",
"android/graphics/text/MeasuredText.cpp",
+ "android_util_AssetManager.cpp",
+ "android_util_StringBlock.cpp",
+ "android_util_XmlBlock.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
"com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
],
@@ -166,27 +176,18 @@
"android_os_HwRemoteBinder.cpp",
"android_os_NativeHandle.cpp",
"android_os_MemoryFile.cpp",
- "android_os_MessageQueue.cpp",
"android_os_Parcel.cpp",
"android_os_SELinux.cpp",
"android_os_SharedMemory.cpp",
- "android_os_SystemClock.cpp",
- "android_os_SystemProperties.cpp",
- "android_os_Trace.cpp",
"android_os_UEventObserver.cpp",
"android_os_VintfObject.cpp",
"android_os_VintfRuntimeInfo.cpp",
"android_net_LocalSocketImpl.cpp",
"android_net_NetUtils.cpp",
- "android_util_AssetManager.cpp",
"android_util_Binder.cpp",
- "android_util_EventLog.cpp",
- "android_util_Log.cpp",
"android_util_StatsLog.cpp",
"android_util_MemoryIntArray.cpp",
"android_util_Process.cpp",
- "android_util_StringBlock.cpp",
- "android_util_XmlBlock.cpp",
"android_util_jar_StrictJarFile.cpp",
"android/graphics/AnimatedImageDrawable.cpp",
"android/graphics/Camera.cpp",
@@ -242,7 +243,6 @@
"android_backup_FileBackupHelperBase.cpp",
"android_backup_BackupHelperDispatcher.cpp",
"android_app_backup_FullBackup.cpp",
- "android_content_res_ApkAssets.cpp",
"android_content_res_ObbScanner.cpp",
"android_content_res_Configuration.cpp",
"android_security_Scrypt.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5611cc4..dad4a28 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -899,20 +899,16 @@
addOption("-Ximage-compiler-option");
addOption("--compiler-filter=speed-profile");
} else {
- // Make sure there is a preloaded-classes file.
- if (!hasFile("/system/etc/preloaded-classes")) {
- ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n",
- strerror(errno));
- return -1;
- }
- addOption("-Ximage-compiler-option");
- addOption("--image-classes=/system/etc/preloaded-classes");
+ ALOGE("Missing boot-image.prof file, /system/etc/boot-image.prof not found: %s\n",
+ strerror(errno));
+ return -1;
+ }
- // If there is a dirty-image-objects file, push it.
- if (hasFile("/system/etc/dirty-image-objects")) {
- addOption("-Ximage-compiler-option");
- addOption("--dirty-image-objects=/system/etc/dirty-image-objects");
- }
+
+ // If there is a dirty-image-objects file, push it.
+ if (hasFile("/system/etc/dirty-image-objects")) {
+ addOption("-Ximage-compiler-option");
+ addOption("--dirty-image-objects=/system/etc/dirty-image-objects");
}
property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index dd5113c..eb70035 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -49,6 +49,10 @@
namespace android {
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
+extern int register_android_content_AssetManager(JNIEnv* env);
+extern int register_android_content_StringBlock(JNIEnv* env);
+extern int register_android_content_XmlBlock(JNIEnv* env);
+extern int register_android_content_res_ApkAssets(JNIEnv* env);
extern int register_android_graphics_Canvas(JNIEnv* env);
extern int register_android_graphics_ColorFilter(JNIEnv* env);
extern int register_android_graphics_ColorSpace(JNIEnv* env);
@@ -66,6 +70,12 @@
extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
extern int register_android_graphics_text_LineBreaker(JNIEnv* env);
extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
+extern int register_android_os_MessageQueue(JNIEnv* env);
+extern int register_android_os_SystemClock(JNIEnv* env);
+extern int register_android_os_SystemProperties(JNIEnv* env);
+extern int register_android_os_Trace(JNIEnv* env);
+extern int register_android_util_EventLog(JNIEnv* env);
+extern int register_android_util_Log(JNIEnv* env);
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_view_RenderNode(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
@@ -78,9 +88,13 @@
};
// Map of all possible class names to register to their corresponding JNI registration function pointer
-// The actual list of registered classes will be determined at runtime via the com.android.tools.layoutlib.create.NativeConfig class
+// The actual list of registered classes will be determined at runtime via the 'native_classes' System property
static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
{"android.animation.PropertyValuesHolder", REG_JNI(register_android_animation_PropertyValuesHolder)},
+ {"android.content.AssetManager", REG_JNI(register_android_content_AssetManager)},
+ {"android.content.StringBlock", REG_JNI(register_android_content_StringBlock)},
+ {"android.content.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
+ {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
{"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)},
{"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
{"android.graphics.ByteBufferStreamAdaptor", REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
@@ -111,11 +125,16 @@
{"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)},
{"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
{"android.graphics.text.MeasuredText", REG_JNI(register_android_graphics_text_MeasuredText)},
+ {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
+ {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
+ {"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
+ {"android.os.Trace", REG_JNI(register_android_os_Trace)},
+ {"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
+ {"android.util.Log", REG_JNI(register_android_util_Log)},
{"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
{"com.android.internal.util.VirtualRefBasePtr", REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
{"com.android.internal.view.animation.NativeInterpolatorFactoryHelper", REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper)},
};
-
// Vector to store the names of classes that need delegates of their native methods
static vector<string> classesToDelegate;
@@ -232,6 +251,8 @@
const char* path = env->GetStringUTFChars(stringPath, 0);
u_setDataDirectory(path);
env->ReleaseStringUTFChars(stringPath, path);
+
+
return JNI_VERSION_1_6;
}
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_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index 9ec7517..ab52314 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -19,7 +19,6 @@
#include "android-base/logging.h"
#include "android-base/properties.h"
-#include "cutils/properties.h"
#include "utils/misc.h"
#include <utils/Log.h>
#include "jni.h"
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 2b471fe..d6d9391 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -18,10 +18,8 @@
#define LOG_TAG "asset"
#include <inttypes.h>
-#include <linux/capability.h>
#include <stdio.h>
#include <sys/stat.h>
-#include <sys/system_properties.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -399,6 +397,7 @@
return array_map;
}
+#ifdef __ANDROID__ // Layoutlib does not support parcel
static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
jlongArray out_offsets) {
off64_t start_offset, length;
@@ -430,6 +429,15 @@
}
return newParcelFileDescriptor(env, file_desc);
}
+#else
+static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
+ jlongArray out_offsets) {
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
+ "Implement me");
+ // never reached
+ return nullptr;
+}
+#endif
static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
return Asset::getGlobalCount();
diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp
index a6adc88..ad35b7d 100644
--- a/core/jni/android_util_Log.cpp
+++ b/core/jni/android_util_Log.cpp
@@ -20,7 +20,6 @@
#include <android-base/macros.h>
#include <assert.h>
-#include <cutils/properties.h>
#include <log/log.h> // For LOGGER_ENTRY_MAX_PAYLOAD.
#include <utils/Log.h>
#include <utils/String8.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/os/system_properties.proto b/core/proto/android/os/system_properties.proto
index d06e1a6..7e26952 100644
--- a/core/proto/android/os/system_properties.proto
+++ b/core/proto/android/os/system_properties.proto
@@ -497,8 +497,7 @@
}
optional Telephony telephony = 38;
- optional string url_legal = 39;
- optional string url_legal_android_privacy = 40;
+ reserved 39, 40; // Removed url_legal* props
message Vendor {
optional string build_date = 1;
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 3402033..3dc74f8 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -38,6 +38,12 @@
// Create package-export.apk, which other packages can use to get
// PRODUCT-agnostic resource data like IDs and type definitions.
export_package_resources: true,
+
+ dist: {
+ targets: [
+ "simulated_device_launcher",
+ ],
+ },
}
// This logic can be removed once robolectric's transition to binary resources is complete
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index aeaccfd..1dd4207 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -129,18 +129,6 @@
android:enabled="false"
android:text="@string/activity_resolver_use_always"
android:onClick="onButtonClick" />
-
- <Button
- android:id="@+id/button_app_settings"
- android:layout_width="wrap_content"
- android:layout_gravity="end"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:text="@string/activity_resolver_app_settings"
- android:onClick="onButtonClick" />
</LinearLayout>
</com.android.internal.widget.ResolverDrawerLayout>
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/strings.xml b/core/res/res/values/strings.xml
index 597f504..6fd855c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3077,12 +3077,20 @@
<string name="whichViewApplicationNamed">Open with %1$s</string>
<!-- Label for a link to a intent resolver dialog to view something -->
<string name="whichViewApplicationLabel">Open</string>
- <!-- Title of intent resolver dialog when selecting a viewer application that opens URI
+ <!-- Title of intent resolver dialog when selecting a browser/application that opens specific URIs
[CHAR LIMIT=128]. -->
- <string name="whichGiveAccessToApplication">Give access to open <xliff:g id="host" example="mail.google.com">%1$s</xliff:g> links with</string>
+ <string name="whichOpenHostLinksWith">Open <xliff:g id="host" example="mail.google.com">%1$s</xliff:g> links with</string>
+ <!-- Title of intent resolver dialog when selecting a browser that opens URI
+ [CHAR LIMIT=128]. -->
+ <string name="whichOpenLinksWith">Open links with</string>
+ <!-- Title of intent resolver dialog when defaulting to a specific browser that opens URI
+ [CHAR LIMIT=128]. -->
+ <string name="whichOpenLinksWithApp">Open links with <xliff:g id="application" example="Chrome">%1$s</xliff:g></string>
+ <!-- Title of intent resolver dialog when defaulting to a specific browser that opens URI
+ [CHAR LIMIT=128]. -->
+ <string name="whichOpenHostLinksWithApp">Open <xliff:g id="host" example="mail.google.com">%1$s</xliff:g> links with <xliff:g id="application" example="Chrome">%2$s</xliff:g></string>
<!-- Title of intent resolver dialog when selecting a viewer application that opens URI
and a previously used application is known [CHAR LIMIT=128]. -->
- <string name="whichGiveAccessToApplicationNamed">Give access to open <xliff:g id="host" example="mail.google.com">%1$s</xliff:g> links with <xliff:g id="application" example="Gmail">%2$s</xliff:g></string>
<!-- Label for a link to an intent resolver dialog to open URI [CHAR LIMIT=18] -->
<string name="whichGiveAccessToApplicationLabel">Give access</string>
<!-- Title of intent resolver dialog when selecting an editor application to run. -->
@@ -4215,6 +4223,10 @@
<string name="activity_resolver_use_always">Always</string>
<!-- Title for a button to choose the currently selected activity
+ as the default in the activity resolver. [CHAR LIMIT=50] -->
+ <string name="activity_resolver_set_always">Set to always open</string>
+
+ <!-- Title for a button to choose the currently selected activity
from the activity resolver to use just this once. [CHAR LIMIT=25] -->
<string name="activity_resolver_use_once">Just once</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1f3ea20..5ca77f9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2242,7 +2242,6 @@
<java-symbol type="id" name="resolver_list" />
<java-symbol type="id" name="button_once" />
<java-symbol type="id" name="button_always" />
- <java-symbol type="id" name="button_app_settings" />
<java-symbol type="integer" name="config_globalActionsKeyTimeout" />
<java-symbol type="integer" name="config_screenshotChordKeyTimeout" />
<java-symbol type="integer" name="config_maxResolverActivityColumns" />
@@ -2624,14 +2623,17 @@
<java-symbol type="bool" name="config_use_voip_mode_for_ims" />
<java-symbol type="attr" name="touchscreenBlocksFocus" />
<java-symbol type="layout" name="resolver_list_with_default" />
- <java-symbol type="string" name="activity_resolver_app_settings" />
+ <java-symbol type="string" name="activity_resolver_set_always" />
+ <java-symbol type="string" name="activity_resolver_use_always" />
<java-symbol type="string" name="whichApplicationNamed" />
<java-symbol type="string" name="whichApplicationLabel" />
<java-symbol type="string" name="whichViewApplication" />
<java-symbol type="string" name="whichViewApplicationNamed" />
<java-symbol type="string" name="whichViewApplicationLabel" />
- <java-symbol type="string" name="whichGiveAccessToApplication" />
- <java-symbol type="string" name="whichGiveAccessToApplicationNamed" />
+ <java-symbol type="string" name="whichOpenHostLinksWith" />
+ <java-symbol type="string" name="whichOpenHostLinksWithApp" />
+ <java-symbol type="string" name="whichOpenLinksWith" />
+ <java-symbol type="string" name="whichOpenLinksWithApp" />
<java-symbol type="string" name="whichGiveAccessToApplicationLabel" />
<java-symbol type="string" name="whichEditApplication" />
<java-symbol type="string" name="whichEditApplicationNamed" />
@@ -3755,6 +3757,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/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
new file mode 100644
index 0000000..f13885e
--- /dev/null
+++ b/core/tests/utiltests/Android.bp
@@ -0,0 +1,38 @@
+//########################################################################
+// Build FrameworksUtilTests package
+//########################################################################
+
+android_test {
+ name: "FrameworksUtilTests",
+
+ // We only want this apk build for tests.
+
+ // Include all test java files.
+ srcs: ["src/**/*.java"] + ["src/android/util/IRemoteMemoryIntArray.aidl"],
+
+ jni_libs: [
+ "libmemoryintarraytest",
+ "libcutils",
+ "libc++",
+ ],
+
+ static_libs: [
+ "androidx.test.rules",
+ "frameworks-base-testutils",
+ "mockito-target-minus-junit4",
+ "androidx.test.ext.junit",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+
+ platform_apis: true,
+
+ certificate: "platform",
+
+ test_suites: ["device-tests"],
+
+}
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
deleted file mode 100644
index 9ef73e9..0000000
--- a/core/tests/utiltests/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#########################################################################
-# Build FrameworksUtilTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SRC_FILES += src/android/util/IRemoteMemoryIntArray.aidl
-
-LOCAL_JNI_SHARED_LIBRARIES := libmemoryintarraytest libcutils libc++
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.rules \
- frameworks-base-testutils \
- mockito-target-minus-junit4 \
- androidx.test.ext.junit
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
-
-LOCAL_PACKAGE_NAME := FrameworksUtilTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
-
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 4f64da7..bbffb35 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -27,15 +27,6 @@
namespace uirenderer {
namespace renderthread {
-static bool IsTransformSupported(int transform) {
- // For now, only support pure rotations, not flip or flip-and-rotate, until we have
- // more time to test them and build sample code. As far as I know we never actually
- // use anything besides pure rotations anyway.
- return transform == 0 || transform == NATIVE_WINDOW_TRANSFORM_ROT_90 ||
- transform == NATIVE_WINDOW_TRANSFORM_ROT_180 ||
- transform == NATIVE_WINDOW_TRANSFORM_ROT_270;
-}
-
static int InvertTransform(int transform) {
switch (transform) {
case NATIVE_WINDOW_TRANSFORM_ROT_90:
@@ -68,28 +59,6 @@
return SkMatrix::I();
}
-void VulkanSurface::ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize,
- const SkISize& maxSize) {
- SkISize& windowSize = windowInfo->size;
-
- // clamp width & height to handle currentExtent of -1 and protect us from broken hints
- if (windowSize.width() < minSize.width() || windowSize.width() > maxSize.width() ||
- windowSize.height() < minSize.height() || windowSize.height() > maxSize.height()) {
- int width = std::min(maxSize.width(), std::max(minSize.width(), windowSize.width()));
- int height = std::min(maxSize.height(), std::max(minSize.height(), windowSize.height()));
- ALOGE("Invalid Window Dimensions [%d, %d]; clamping to [%d, %d]", windowSize.width(),
- windowSize.height(), width, height);
- windowSize.set(width, height);
- }
-
- windowInfo->actualSize = windowSize;
- if (windowInfo->transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- windowInfo->actualSize.set(windowSize.height(), windowSize.width());
- }
-
- windowInfo->preTransform = GetPreTransformMatrix(windowInfo->size, windowInfo->transform);
-}
-
static bool ConnectAndSetWindowDefaults(ANativeWindow* window) {
ATRACE_CALL();
@@ -125,6 +94,24 @@
return false;
}
+ // Let consumer drive the size of the buffers.
+ err = native_window_set_buffers_dimensions(window, 0, 0);
+ if (err != 0) {
+ ALOGE("native_window_set_buffers_dimensions(0,0) failed: %s (%d)", strerror(-err), err);
+ return false;
+ }
+
+ // Enable auto prerotation, so when buffer size is driven by the consumer
+ // and the transform hint specifies a 90 or 270 degree rotation, the width
+ // and height used for buffer pre-allocation and dequeueBuffer will be
+ // additionally swapped.
+ err = native_window_set_auto_prerotation(window, true);
+ if (err != 0) {
+ ALOGE("VulkanSurface::UpdateWindow() native_window_set_auto_prerotation failed: %s (%d)",
+ strerror(-err), err);
+ return false;
+ }
+
return true;
}
@@ -132,10 +119,6 @@
SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
GrContext* grContext, const VulkanManager& vkManager,
uint32_t extraBuffers) {
- // TODO(http://b/134182502)
- const SkISize minSize = SkISize::Make(1, 1);
- const SkISize maxSize = SkISize::Make(4096, 4096);
-
// Connect and set native window to default configurations.
if (!ConnectAndSetWindowDefaults(window)) {
return nullptr;
@@ -144,7 +127,7 @@
// Initialize WindowInfo struct.
WindowInfo windowInfo;
if (!InitializeWindowInfoStruct(window, colorMode, colorType, colorSpace, vkManager,
- extraBuffers, minSize, maxSize, &windowInfo)) {
+ extraBuffers, &windowInfo)) {
return nullptr;
}
@@ -153,15 +136,14 @@
return nullptr;
}
- return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext);
+ return new VulkanSurface(window, windowInfo, 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) {
+ uint32_t extraBuffers, WindowInfo* outWindowInfo) {
ATRACE_CALL();
int width, height;
@@ -185,7 +167,13 @@
}
outWindowInfo->transform = query_value;
- ComputeWindowSizeAndTransform(outWindowInfo, minSize, maxSize);
+ outWindowInfo->actualSize = outWindowInfo->size;
+ if (outWindowInfo->transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ outWindowInfo->actualSize.set(outWindowInfo->size.height(), outWindowInfo->size.width());
+ }
+
+ outWindowInfo->preTransform =
+ GetPreTransformMatrix(outWindowInfo->size, outWindowInfo->transform);
err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
if (err != 0 || query_value < 0) {
@@ -290,15 +278,6 @@
return false;
}
- const SkISize& size = windowInfo.actualSize;
- err = native_window_set_buffers_dimensions(window, size.width(), size.height());
- if (err != 0) {
- ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) "
- "failed: %s (%d)",
- size.width(), size.height(), strerror(-err), err);
- return false;
- }
-
// native_window_set_buffers_transform() expects the transform the app is requesting that
// the compositor perform during composition. With native windows, pre-transform works by
// rendering with the same transform the compositor is applying (as in Vulkan), but
@@ -331,12 +310,8 @@
}
VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
- SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext)
- : mNativeWindow(window)
- , mWindowInfo(windowInfo)
- , mGrContext(grContext)
- , mMinWindowSize(minWindowSize)
- , mMaxWindowSize(maxWindowSize) {}
+ GrContext* grContext)
+ : mNativeWindow(window), mWindowInfo(windowInfo), mGrContext(grContext) {}
VulkanSurface::~VulkanSurface() {
releaseBuffers();
@@ -379,58 +354,51 @@
// value at the end of the function if everything dequeued correctly.
mCurrentBufferInfo = nullptr;
- // check if the native window has been resized or rotated and update accordingly
- SkISize newSize = SkISize::MakeEmpty();
+ // Query the transform hint synced from the initial Surface connect or last queueBuffer. The
+ // auto prerotation on the buffer is based on the same transform hint in use by the producer.
int transformHint = 0;
- mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth);
- mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &newSize.fHeight);
- mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
- if (newSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) {
- WindowInfo newWindowInfo = mWindowInfo;
- newWindowInfo.size = newSize;
- newWindowInfo.transform = IsTransformSupported(transformHint) ? transformHint : 0;
- ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
+ int err =
+ mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
- int err = 0;
- if (newWindowInfo.actualSize != mWindowInfo.actualSize) {
- // reset the native buffers and update the window
- err = native_window_set_buffers_dimensions(mNativeWindow.get(),
- newWindowInfo.actualSize.width(),
- newWindowInfo.actualSize.height());
- if (err != 0) {
- ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
- newWindowInfo.actualSize.width(), newWindowInfo.actualSize.height(),
- strerror(-err), err);
- return nullptr;
- }
- // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The
- // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer.
- releaseBuffers();
- // TODO should we ask the nativewindow to allocate buffers?
- }
-
- if (newWindowInfo.transform != mWindowInfo.transform) {
- err = native_window_set_buffers_transform(mNativeWindow.get(),
- InvertTransform(newWindowInfo.transform));
- if (err != 0) {
- ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
- newWindowInfo.transform, strerror(-err), err);
- newWindowInfo.transform = mWindowInfo.transform;
- ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
- }
- }
-
- mWindowInfo = newWindowInfo;
- }
-
+ // Since auto pre-rotation is enabled, dequeueBuffer to get the consumer driven buffer size
+ // from ANativeWindowBuffer.
ANativeWindowBuffer* buffer;
int fence_fd;
- int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd);
+ err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd);
if (err != 0) {
ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
return nullptr;
}
+ SkISize actualSize = SkISize::Make(buffer->width, buffer->height);
+ if (actualSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) {
+ if (actualSize != mWindowInfo.actualSize) {
+ // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The
+ // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer.
+ mWindowInfo.actualSize = actualSize;
+ releaseBuffers();
+ }
+
+ if (transformHint != mWindowInfo.transform) {
+ err = native_window_set_buffers_transform(mNativeWindow.get(),
+ InvertTransform(transformHint));
+ if (err != 0) {
+ ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", transformHint,
+ strerror(-err), err);
+ mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
+ return nullptr;
+ }
+ mWindowInfo.transform = transformHint;
+ }
+
+ mWindowInfo.size = actualSize;
+ if (mWindowInfo.transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ mWindowInfo.size.set(actualSize.height(), actualSize.width());
+ }
+
+ mWindowInfo.preTransform = GetPreTransformMatrix(mWindowInfo.size, mWindowInfo.transform);
+ }
+
uint32_t idx;
for (idx = 0; idx < mWindowInfo.bufferCount; idx++) {
if (mNativeBuffers[idx].buffer.get() == buffer) {
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index 54007e7..5fa860a 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -101,16 +101,12 @@
SkMatrix preTransform;
};
- VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, SkISize minWindowSize,
- SkISize maxWindowSize, GrContext* grContext);
+ VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, 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);
void releaseBuffers();
// TODO: Just use a vector?
@@ -122,11 +118,8 @@
uint32_t mPresentCount = 0;
NativeBufferInfo* mCurrentBufferInfo = nullptr;
-
- const SkISize mMinWindowSize;
- const SkISize mMaxWindowSize;
};
} /* namespace renderthread */
} /* namespace uirenderer */
-} /* namespace android */
\ No newline at end of file
+} /* namespace android */
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 18a41ba..358e2cd 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -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() {
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/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/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/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 097f92c..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
@@ -4747,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 */);
@@ -4760,6 +4760,11 @@
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
@@ -5268,6 +5273,7 @@
onUpdateRowStates();
mEntryManager.updateNotifications();
+ updateVisibility();
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
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/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/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/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/LmkdConnection.java b/services/core/java/com/android/server/am/LmkdConnection.java
new file mode 100644
index 0000000..d1e09db
--- /dev/null
+++ b/services/core/java/com/android/server/am/LmkdConnection.java
@@ -0,0 +1,293 @@
+/*
+ * 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.server.am;
+
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.os.MessageQueue;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import libcore.io.IoUtils;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Lmkd connection to communicate with lowmemorykiller daemon.
+ */
+public class LmkdConnection {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdConnection" : TAG_AM;
+
+ // lmkd reply max size in bytes
+ private static final int LMKD_REPLY_MAX_SIZE = 8;
+
+ // connection listener interface
+ interface LmkdConnectionListener {
+ public boolean onConnect(OutputStream ostream);
+ public void onDisconnect();
+ /**
+ * Check if received reply was expected (reply to an earlier request)
+ *
+ * @param replyBuf The buffer provided in exchange() to receive the reply.
+ * It can be used by exchange() caller to store reply-specific
+ * tags for later use in isReplyExpected() to verify if
+ * received packet is the expected reply.
+ * @param dataReceived The buffer holding received data
+ * @param receivedLen Size of the data received
+ */
+ public boolean isReplyExpected(ByteBuffer replyBuf, ByteBuffer dataReceived,
+ int receivedLen);
+ }
+
+ private final MessageQueue mMsgQueue;
+
+ // lmkd connection listener
+ private final LmkdConnectionListener mListener;
+
+ // mutex to synchronize access to the socket
+ private final Object mLmkdSocketLock = new Object();
+
+ // socket to communicate with lmkd
+ @GuardedBy("mLmkdSocketLock")
+ private LocalSocket mLmkdSocket = null;
+
+ // socket I/O streams
+ @GuardedBy("mLmkdSocketLock")
+ private OutputStream mLmkdOutputStream = null;
+ @GuardedBy("mLmkdSocketLock")
+ private InputStream mLmkdInputStream = null;
+
+ // buffer to store incoming data
+ private final ByteBuffer mInputBuf =
+ ByteBuffer.allocate(LMKD_REPLY_MAX_SIZE);
+
+ // object to protect mReplyBuf and to wait/notify when reply is received
+ private final Object mReplyBufLock = new Object();
+
+ // reply buffer
+ @GuardedBy("mReplyBufLock")
+ private ByteBuffer mReplyBuf = null;
+
+ //////////////////// END FIELDS ////////////////////
+
+ LmkdConnection(MessageQueue msgQueue, LmkdConnectionListener listener) {
+ mMsgQueue = msgQueue;
+ mListener = listener;
+ }
+
+ public boolean connect() {
+ synchronized (mLmkdSocketLock) {
+ if (mLmkdSocket != null) {
+ return true;
+ }
+ // temporary sockets and I/O streams
+ final LocalSocket socket = openSocket();
+
+ if (socket == null) {
+ Slog.w(TAG, "Failed to connect to lowmemorykiller, retry later");
+ return false;
+ }
+
+ final OutputStream ostream;
+ final InputStream istream;
+ try {
+ ostream = socket.getOutputStream();
+ istream = socket.getInputStream();
+ } catch (IOException ex) {
+ IoUtils.closeQuietly(socket);
+ return false;
+ }
+ // execute onConnect callback
+ if (mListener != null && !mListener.onConnect(ostream)) {
+ Slog.w(TAG, "Failed to communicate with lowmemorykiller, retry later");
+ IoUtils.closeQuietly(socket);
+ return false;
+ }
+ // connection established
+ mLmkdSocket = socket;
+ mLmkdOutputStream = ostream;
+ mLmkdInputStream = istream;
+ mMsgQueue.addOnFileDescriptorEventListener(mLmkdSocket.getFileDescriptor(),
+ EVENT_INPUT | EVENT_ERROR,
+ new MessageQueue.OnFileDescriptorEventListener() {
+ public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+ return fileDescriptorEventHandler(fd, events);
+ }
+ }
+ );
+ mLmkdSocketLock.notifyAll();
+ }
+ return true;
+ }
+
+ private int fileDescriptorEventHandler(FileDescriptor fd, int events) {
+ if (mListener == null) {
+ return 0;
+ }
+ if ((events & EVENT_INPUT) != 0) {
+ processIncomingData();
+ }
+ if ((events & EVENT_ERROR) != 0) {
+ synchronized (mLmkdSocketLock) {
+ // stop listening on this socket
+ mMsgQueue.removeOnFileDescriptorEventListener(
+ mLmkdSocket.getFileDescriptor());
+ IoUtils.closeQuietly(mLmkdSocket);
+ mLmkdSocket = null;
+ }
+ // wake up reply waiters if any
+ synchronized (mReplyBufLock) {
+ if (mReplyBuf != null) {
+ mReplyBuf = null;
+ mReplyBufLock.notifyAll();
+ }
+ }
+ // notify listener
+ mListener.onDisconnect();
+ return 0;
+ }
+ return (EVENT_INPUT | EVENT_ERROR);
+ }
+
+ private void processIncomingData() {
+ int len = read(mInputBuf);
+ if (len > 0) {
+ synchronized (mReplyBufLock) {
+ if (mReplyBuf != null) {
+ if (mListener.isReplyExpected(mReplyBuf, mInputBuf, len)) {
+ // copy into reply buffer
+ mReplyBuf.put(mInputBuf.array(), 0, len);
+ mReplyBuf.rewind();
+ // wakeup the waiting thread
+ mReplyBufLock.notifyAll();
+ } else {
+ // received asynchronous or unexpected packet
+ // treat this as an error
+ mReplyBuf = null;
+ mReplyBufLock.notifyAll();
+ Slog.e(TAG, "Received unexpected packet from lmkd");
+ }
+ } else {
+ // received asynchronous communication from lmkd
+ // we don't support this yet
+ Slog.w(TAG, "Received an asynchronous packet from lmkd");
+ }
+ }
+ }
+ }
+
+ public boolean isConnected() {
+ synchronized (mLmkdSocketLock) {
+ return (mLmkdSocket != null);
+ }
+ }
+
+ public boolean waitForConnection(long timeoutMs) {
+ synchronized (mLmkdSocketLock) {
+ if (mLmkdSocket != null) {
+ return true;
+ }
+ try {
+ mLmkdSocketLock.wait(timeoutMs);
+ return (mLmkdSocket != null);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+ }
+
+ private LocalSocket openSocket() {
+ final LocalSocket socket;
+
+ try {
+ socket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
+ socket.connect(
+ new LocalSocketAddress("lmkd",
+ LocalSocketAddress.Namespace.RESERVED));
+ } catch (IOException ex) {
+ Slog.e(TAG, "Connection failed: " + ex.toString());
+ return null;
+ }
+ return socket;
+ }
+
+ private boolean write(ByteBuffer buf) {
+ synchronized (mLmkdSocketLock) {
+ try {
+ mLmkdOutputStream.write(buf.array(), 0, buf.position());
+ } catch (IOException ex) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private int read(ByteBuffer buf) {
+ synchronized (mLmkdSocketLock) {
+ try {
+ return mLmkdInputStream.read(buf.array(), 0, buf.array().length);
+ } catch (IOException ex) {
+ }
+ return -1;
+ }
+ }
+
+ /**
+ * Exchange a request/reply packets with lmkd
+ *
+ * @param req The buffer holding the request data to be sent
+ * @param repl The buffer to receive the reply
+ */
+ public boolean exchange(ByteBuffer req, ByteBuffer repl) {
+ if (repl == null) {
+ return write(req);
+ }
+
+ boolean result = false;
+ // set reply buffer to user-defined one to fill it
+ synchronized (mReplyBufLock) {
+ mReplyBuf = repl;
+
+ if (write(req)) {
+ try {
+ // wait for the reply
+ mReplyBufLock.wait();
+ result = (mReplyBuf != null);
+ } catch (InterruptedException ie) {
+ result = false;
+ }
+ }
+
+ // reset reply buffer
+ mReplyBuf = null;
+ }
+ return result;
+ }
+}
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/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 9346ab5..ffbc6b3 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -57,8 +57,6 @@
import android.content.pm.IPackageManager;
import android.content.res.Resources;
import android.graphics.Point;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
import android.os.AppZygote;
import android.os.Binder;
import android.os.Build;
@@ -67,6 +65,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.MessageQueue;
import android.os.Process;
import android.os.RemoteException;
import android.os.StrictMode;
@@ -117,11 +116,6 @@
/**
* Activity manager code dealing with processes.
- *
- * Method naming convention:
- * <ul>
- * <li> Methods suffixed with "LS" should be called within the {@link #sLmkdSocketLock} lock.
- * </ul>
*/
public final class ProcessList {
static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
@@ -268,6 +262,9 @@
static final byte LMK_PROCPURGE = 3;
static final byte LMK_GETKILLCNT = 4;
+ // lmkd reconnect delay in msecs
+ private final static long LMDK_RECONNECT_DELAY_MS = 1000;
+
ActivityManagerService mService = null;
// To kill process groups asynchronously
@@ -302,16 +299,9 @@
private boolean mHaveDisplaySize;
- private static Object sLmkdSocketLock = new Object();
+ private static LmkdConnection sLmkdConnection = null;
- @GuardedBy("sLmkdSocketLock")
- private static LocalSocket sLmkdSocket;
-
- @GuardedBy("sLmkdSocketLock")
- private static OutputStream sLmkdOutputStream;
-
- @GuardedBy("sLmkdSocketLock")
- private static InputStream sLmkdInputStream;
+ private boolean mOomLevelsSet = false;
/**
* Temporary to avoid allocations. Protected by main lock.
@@ -536,6 +526,7 @@
final class KillHandler extends Handler {
static final int KILL_PROCESS_GROUP_MSG = 4000;
+ static final int LMDK_RECONNECT_MSG = 4001;
public KillHandler(Looper looper) {
super(looper, null, true);
@@ -549,6 +540,15 @@
Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
+ case LMDK_RECONNECT_MSG:
+ if (!sLmkdConnection.connect()) {
+ Slog.i(TAG, "Failed to connect to lmkd, retry after " +
+ LMDK_RECONNECT_DELAY_MS + " ms");
+ // retry after LMDK_RECONNECT_DELAY_MS
+ sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage(
+ KillHandler.LMDK_RECONNECT_MSG), LMDK_RECONNECT_DELAY_MS);
+ }
+ break;
default:
super.handleMessage(msg);
@@ -574,6 +574,30 @@
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
sKillThread.start();
sKillHandler = new KillHandler(sKillThread.getLooper());
+ sLmkdConnection = new LmkdConnection(sKillThread.getLooper().getQueue(),
+ new LmkdConnection.LmkdConnectionListener() {
+ @Override
+ public boolean onConnect(OutputStream ostream) {
+ Slog.i(TAG, "Connection with lmkd established");
+ return onLmkdConnect(ostream);
+ }
+ @Override
+ public void onDisconnect() {
+ Slog.w(TAG, "Lost connection to lmkd");
+ // start reconnection after delay to let lmkd restart
+ sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage(
+ KillHandler.LMDK_RECONNECT_MSG), LMDK_RECONNECT_DELAY_MS);
+ }
+ @Override
+ public boolean isReplyExpected(ByteBuffer replyBuf,
+ ByteBuffer dataReceived, int receivedLen) {
+ // compare the preambule (currently one integer) to check if
+ // this is the reply packet we are waiting for
+ return (receivedLen == replyBuf.array().length &&
+ dataReceived.getInt(0) == replyBuf.getInt(0));
+ }
+ }
+ );
}
}
@@ -679,6 +703,7 @@
writeLmkd(buf, null);
SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
+ mOomLevelsSet = true;
}
// GB: 2048,3072,4096,6144,7168,8192
// HC: 8192,10240,12288,14336,16384,20480
@@ -1218,93 +1243,50 @@
buf.putInt(LMK_GETKILLCNT);
buf.putInt(min_oom_adj);
buf.putInt(max_oom_adj);
- if (writeLmkd(buf, repl)) {
- int i = repl.getInt();
- if (i != LMK_GETKILLCNT) {
- Slog.e("ActivityManager", "Failed to get kill count, code mismatch");
- return null;
- }
+ // indicate what we are waiting for
+ repl.putInt(LMK_GETKILLCNT);
+ repl.rewind();
+ if (writeLmkd(buf, repl) && repl.getInt() == LMK_GETKILLCNT) {
return new Integer(repl.getInt());
}
return null;
}
- @GuardedBy("sLmkdSocketLock")
- private static boolean openLmkdSocketLS() {
+ public boolean onLmkdConnect(OutputStream ostream) {
try {
- sLmkdSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
- sLmkdSocket.connect(
- new LocalSocketAddress("lmkd",
- LocalSocketAddress.Namespace.RESERVED));
- sLmkdOutputStream = sLmkdSocket.getOutputStream();
- sLmkdInputStream = sLmkdSocket.getInputStream();
- } catch (IOException ex) {
- Slog.w(TAG, "lowmemorykiller daemon socket open failed");
- sLmkdSocket = null;
- return false;
- }
-
- return true;
- }
-
- // Never call directly, use writeLmkd() instead
- @GuardedBy("sLmkdSocketLock")
- private static boolean writeLmkdCommandLS(ByteBuffer buf) {
- try {
- sLmkdOutputStream.write(buf.array(), 0, buf.position());
- } catch (IOException ex) {
- Slog.w(TAG, "Error writing to lowmemorykiller socket");
- IoUtils.closeQuietly(sLmkdSocket);
- sLmkdSocket = null;
- return false;
- }
- return true;
- }
-
- // Never call directly, use writeLmkd() instead
- @GuardedBy("sLmkdSocketLock")
- private static boolean readLmkdReplyLS(ByteBuffer buf) {
- int len;
- try {
- len = sLmkdInputStream.read(buf.array(), 0, buf.array().length);
- if (len == buf.array().length) {
- return true;
+ // Purge any previously registered pids
+ ByteBuffer buf = ByteBuffer.allocate(4);
+ buf.putInt(LMK_PROCPURGE);
+ ostream.write(buf.array(), 0, buf.position());
+ if (mOomLevelsSet) {
+ // Reset oom_adj levels
+ buf = ByteBuffer.allocate(4 * (2 * mOomAdj.length + 1));
+ buf.putInt(LMK_TARGET);
+ for (int i = 0; i < mOomAdj.length; i++) {
+ buf.putInt((mOomMinFree[i] * 1024)/PAGE_SIZE);
+ buf.putInt(mOomAdj[i]);
+ }
+ ostream.write(buf.array(), 0, buf.position());
}
} catch (IOException ex) {
- Slog.w(TAG, "Error reading from lowmemorykiller socket");
+ return false;
}
-
- IoUtils.closeQuietly(sLmkdSocket);
- sLmkdSocket = null;
- return false;
+ return true;
}
private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
- synchronized (sLmkdSocketLock) {
- for (int i = 0; i < 3; i++) {
- if (sLmkdSocket == null) {
- if (openLmkdSocketLS() == false) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException ie) {
- }
- continue;
- }
+ if (!sLmkdConnection.isConnected()) {
+ // try to connect immediately and then keep retrying
+ sKillHandler.sendMessage(
+ sKillHandler.obtainMessage(KillHandler.LMDK_RECONNECT_MSG));
- // Purge any previously registered pids
- ByteBuffer purge_buf = ByteBuffer.allocate(4);
- purge_buf.putInt(LMK_PROCPURGE);
- if (writeLmkdCommandLS(purge_buf) == false) {
- // Write failed, skip the rest and retry
- continue;
- }
- }
- if (writeLmkdCommandLS(buf) && (repl == null || readLmkdReplyLS(repl))) {
- return true;
- }
+ // wait for connection retrying 3 times (up to 3 seconds)
+ if (!sLmkdConnection.waitForConnection(3 * LMDK_RECONNECT_DELAY_MS)) {
+ return false;
}
}
- return false;
+
+ return sLmkdConnection.exchange(buf, repl);
}
static void killProcessGroup(int uid, int pid) {
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/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/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 440676a..df4674c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1498,6 +1498,11 @@
Slog.e(TAG, "Callback cannot be null");
return;
}
+ if (isPowerStandby()) {
+ Slog.e(TAG, "Device is in standby. Not handling deviceSelect");
+ invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
+ return;
+ }
HdmiCecLocalDeviceTv tv = tv();
if (tv == null) {
if (!mAddressAllocated) {
@@ -1540,6 +1545,11 @@
Slog.e(TAG, "Callback cannot be null");
return;
}
+ if (isPowerStandby()) {
+ Slog.e(TAG, "Device is in standby. Not handling portSelect");
+ invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
+ return;
+ }
HdmiCecLocalDeviceTv tv = tv();
if (tv != null) {
tv.doManualPortSwitching(portId, callback);
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/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/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 505e89d..de3e89f 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -45,10 +45,9 @@
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.Process;
-import android.os.UserHandle; // duped to avoid merge conflict
-import android.os.UserManager; // out of order to avoid merge conflict
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IntArray;
@@ -326,7 +325,7 @@
}
@Override
- public ParceledListSlice<RollbackInfo> getRecentlyExecutedRollbacks() {
+ public ParceledListSlice<RollbackInfo> getRecentlyCommittedRollbacks() {
enforceManageRollbacks("getRecentlyCommittedRollbacks");
synchronized (mLock) {
@@ -345,7 +344,7 @@
@Override
public void commitRollback(int rollbackId, ParceledListSlice causePackages,
String callerPackageName, IntentSender statusReceiver) {
- enforceManageRollbacks("executeRollback");
+ enforceManageRollbacks("commitRollback");
final int callingUid = Binder.getCallingUid();
AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
@@ -575,6 +574,20 @@
}
}
+ @Override
+ public void blockRollbackManager(long millis) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
+ "blockRollbackManager");
+ getHandler().post(() -> {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ // ignored.
+ }
+ });
+ }
+
void onUnlockUser(int userId) {
getHandler().post(() -> {
final List<RollbackData> rollbacks;
@@ -677,8 +690,7 @@
/**
* Load rollback data from storage if it has not already been loaded.
- * After calling this funciton, mAvailableRollbacks and
- * mRecentlyExecutedRollbacks will be non-null.
+ * After calling this function, mRollbacks will be non-null.
*/
private void ensureRollbackDataLoaded() {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 8b332d2..55f062b 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -1032,11 +1032,13 @@
// must always grant permissions on behalf of someone explicit.
final int callingAppId = UserHandle.getAppId(callingUid);
if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {
- if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) {
+ if ("com.android.settings.files".equals(grantUri.uri.getAuthority())
+ || "com.android.settings.module_licenses".equals(grantUri.uri.getAuthority())) {
// Exempted authority for
// 1. cropping user photos and sharing a generated license html
// file in Settings app
// 2. sharing a generated license html file in TvSettings app
+ // 3. Sharing module license files from Settings app
} else {
Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
+ " grant to " + grantUri + "; use startActivityAsCaller() instead");
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 156fb98..157e15a 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1302,6 +1302,11 @@
return;
}
+ getDisplay().positionChildAtBottom(this, reason);
+ if (task != null) {
+ insertTaskAtBottom(task);
+ }
+
/**
* The intent behind moving a primary split screen stack to the back is usually to hide
* behind the home stack. Exit split screen in this case.
@@ -1309,11 +1314,6 @@
if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
setWindowingMode(WINDOWING_MODE_UNDEFINED);
}
-
- getDisplay().positionChildAtBottom(this, reason);
- if (task != null) {
- insertTaskAtBottom(task);
- }
}
boolean isFocusable() {
@@ -1388,13 +1388,12 @@
}
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
- + taskIntent.getComponent().flattenToShortString()
+ + (task.realActivity != null ? task.realActivity.flattenToShortString() : "")
+ "/aff=" + r.getTaskRecord().rootAffinity + " to new cls="
+ intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
// TODO Refactor to remove duplications. Check if logic can be simplified.
- if (taskIntent != null && taskIntent.getComponent() != null &&
- taskIntent.getComponent().compareTo(cls) == 0 &&
- Objects.equals(documentData, taskDocumentData)) {
+ if (task.realActivity != null && task.realActivity.compareTo(cls) == 0
+ && Objects.equals(documentData, taskDocumentData)) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
//dump();
if (DEBUG_TASKS) Slog.d(TAG_TASKS,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 16dd555..6e3f03d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3918,10 +3918,10 @@
// Caller wants the current split-screen primary stack to be the top stack after
// it goes fullscreen, so move it to the front.
stack.moveToFront("dismissSplitScreenMode");
- } else if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
+ } else {
// In this case the current split-screen primary stack shouldn't be the top
- // stack after it goes fullscreen, but it current has focus, so we move the
- // focus to the top-most split-screen secondary stack next to it.
+ // stack after it goes fullscreen, so we move the focus to the top-most
+ // split-screen secondary stack next to it.
final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
if (otherStack != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 57ed92d..72a7fcf 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -30,16 +30,21 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_LEFT_GESTURES;
+import static android.view.InsetsState.TYPE_RIGHT_GESTURES;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.View.GONE;
+import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_TOP;
@@ -135,6 +140,7 @@
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+import static com.android.server.wm.utils.RegionUtils.forEachRect;
import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
import android.animation.AnimationHandler;
@@ -323,6 +329,7 @@
private final RemoteCallbackList<ISystemGestureExclusionListener>
mSystemGestureExclusionListeners = new RemoteCallbackList<>();
private final Region mSystemGestureExclusion = new Region();
+ private int mSystemGestureExclusionLimit;
/**
* For default display it contains real metrics, empty for others.
@@ -893,6 +900,8 @@
mWallpaperController = new WallpaperController(mWmService, this);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
+ mSystemGestureExclusionLimit = mWmService.mSystemGestureExclusionLimitDp
+ * mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
@@ -1548,8 +1557,8 @@
longSize = height;
}
- final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
- final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
+ final int shortSizeDp = shortSize * DENSITY_DEFAULT / mBaseDisplayDensity;
+ final int longSizeDp = longSize * DENSITY_DEFAULT / mBaseDisplayDensity;
mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
@@ -2199,6 +2208,18 @@
onDisplayChanged(this);
}
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ super.onDisplayChanged(dc);
+ updateSystemGestureExclusionLimit();
+ }
+
+ void updateSystemGestureExclusionLimit() {
+ mSystemGestureExclusionLimit = mWmService.mSystemGestureExclusionLimitDp
+ * mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
+ updateSystemGestureExclusion();
+ }
+
void initializeDisplayBaseInfo() {
final DisplayManagerInternal displayManagerInternal = mWmService.mDisplayManagerInternal;
if (displayManagerInternal != null) {
@@ -5130,24 +5151,35 @@
@VisibleForTesting
Region calculateSystemGestureExclusion() {
+ final Region unhandled = Region.obtain();
+ unhandled.set(0, 0, mDisplayFrames.mDisplayWidth, mDisplayFrames.mDisplayHeight);
+
+ final Rect leftEdge = mInsetsStateController.getSourceProvider(TYPE_LEFT_GESTURES)
+ .getSource().getFrame();
+ final Rect rightEdge = mInsetsStateController.getSourceProvider(TYPE_RIGHT_GESTURES)
+ .getSource().getFrame();
+
final Region global = Region.obtain();
final Region touchableRegion = Region.obtain();
final Region local = Region.obtain();
+ final int[] remainingLeftRight =
+ {mSystemGestureExclusionLimit, mSystemGestureExclusionLimit};
// Traverse all windows bottom up to assemble the gesture exclusion rects.
// For each window, we only take the rects that fall within its touchable region.
forAllWindows(w -> {
if (w.cantReceiveTouchInput() || !w.isVisible()
- || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0) {
+ || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0
+ || unhandled.isEmpty()) {
return;
}
final boolean modal =
(w.mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
- // Only keep the exclusion zones from the windows behind where the current window
- // isn't touchable.
+ // Get the touchable region of the window, and intersect with where the screen is still
+ // touchable, i.e. touchable regions on top are not covering it yet.
w.getTouchableRegion(touchableRegion);
- global.op(touchableRegion, Op.DIFFERENCE);
+ touchableRegion.op(unhandled, Op.INTERSECT);
rectListToRegion(w.getSystemGestureExclusion(), local);
@@ -5159,13 +5191,78 @@
// A window can only exclude system gestures where it is actually touchable
local.op(touchableRegion, Op.INTERSECT);
- global.op(local, Op.UNION);
- }, false /* topToBottom */);
+ // Apply restriction if necessary.
+ if (needsGestureExclusionRestrictions(w, mLastDispatchedSystemUiVisibility)) {
+
+ // Processes the region along the left edge.
+ remainingLeftRight[0] = addToGlobalAndConsumeLimit(local, global, leftEdge,
+ remainingLeftRight[0]);
+
+ // Processes the region along the right edge.
+ remainingLeftRight[1] = addToGlobalAndConsumeLimit(local, global, rightEdge,
+ remainingLeftRight[1]);
+
+ // Adds the middle (unrestricted area)
+ final Region middle = Region.obtain(local);
+ middle.op(leftEdge, Op.DIFFERENCE);
+ middle.op(rightEdge, Op.DIFFERENCE);
+ global.op(middle, Op.UNION);
+ middle.recycle();
+ } else {
+ global.op(local, Op.UNION);
+ }
+ unhandled.op(touchableRegion, Op.DIFFERENCE);
+ }, true /* topToBottom */);
local.recycle();
touchableRegion.recycle();
+ unhandled.recycle();
return global;
}
+ /**
+ * @return Whether gesture exclusion area should be restricted from the window depending on the
+ * current SystemUI visibility flags.
+ */
+ private static boolean needsGestureExclusionRestrictions(WindowState win, int sysUiVisibility) {
+ final int type = win.mAttrs.type;
+ final int stickyHideNavFlags =
+ SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ final boolean stickyHideNav =
+ (sysUiVisibility & stickyHideNavFlags) == stickyHideNavFlags;
+ return !stickyHideNav && type != TYPE_INPUT_METHOD && type != TYPE_STATUS_BAR
+ && win.getActivityType() != ACTIVITY_TYPE_HOME;
+ }
+
+ /**
+ * Adds a local gesture exclusion area to the global area while applying a limit per edge.
+ *
+ * @param local The gesture exclusion area to add.
+ * @param global The destination.
+ * @param edge Only processes the part in that region.
+ * @param limit How much limit in pixels we have.
+ * @return How much of the limit are remaining.
+ */
+ private static int addToGlobalAndConsumeLimit(Region local, Region global, Rect edge,
+ int limit) {
+ final Region r = Region.obtain(local);
+ r.op(edge, Op.INTERSECT);
+
+ final int[] remaining = {limit};
+ forEachRect(r, rect -> {
+ if (remaining[0] <= 0) {
+ return;
+ }
+ final int height = rect.height();
+ if (height > remaining[0]) {
+ rect.bottom = rect.top + remaining[0];
+ }
+ remaining[0] -= height;
+ global.op(rect, Op.UNION);
+ });
+ r.recycle();
+ return remaining[0];
+ }
+
void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener) {
mSystemGestureExclusionListeners.register(listener);
final boolean changed;
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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2bf24f6..54f712c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -32,6 +32,7 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -155,6 +156,7 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Looper;
@@ -176,6 +178,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -216,6 +219,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;
@@ -381,6 +385,8 @@
private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000;
+ private static final int MIN_GESTURE_EXCLUSION_LIMIT_DP = 200;
+
final WindowTracing mWindowTracing;
final private KeyguardDisableHandler mKeyguardDisableHandler;
@@ -839,6 +845,8 @@
final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>();
boolean mWindowsChanged = false;
+ int mSystemGestureExclusionLimitDp;
+
public interface WindowChangeListener {
public void windowsChanged();
public void focusChanged();
@@ -1133,6 +1141,21 @@
this, mInputManager, mActivityTaskManager, mH.getLooper());
mDragDropController = new DragDropController(this, mH.getLooper());
+ mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ new HandlerExecutor(mH), properties -> {
+ synchronized (mGlobalLock) {
+ final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
+ properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+ if (mSystemGestureExclusionLimitDp != exclusionLimitDp) {
+ mSystemGestureExclusionLimitDp = exclusionLimitDp;
+ mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
+ }
+ }
+ });
+
LocalServices.addService(WindowManagerInternal.class, new LocalService());
}
@@ -7748,4 +7771,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/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java
index 1458440..8cd6f88 100644
--- a/services/core/java/com/android/server/wm/utils/RegionUtils.java
+++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java
@@ -18,8 +18,10 @@
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.RegionIterator;
import java.util.List;
+import java.util.function.Consumer;
/**
* Utility methods to handle Regions.
@@ -42,4 +44,18 @@
outRegion.union(rects.get(i));
}
}
+
+ /**
+ * Applies actions on each rect contained within a {@code Region}.
+ *
+ * @param region the given region.
+ * @param rectConsumer the action holder.
+ */
+ public static void forEachRect(Region region, Consumer<Rect> rectConsumer) {
+ final RegionIterator it = new RegionIterator(region);
+ final Rect rect = new Rect();
+ while (it.next(rect)) {
+ rectConsumer.accept(rect);
+ }
+ }
}
diff --git a/services/devicepolicy/TEST_MAPPING b/services/devicepolicy/TEST_MAPPING
index febfa17..a5ee3e2 100644
--- a/services/devicepolicy/TEST_MAPPING
+++ b/services/devicepolicy/TEST_MAPPING
@@ -1,12 +1,20 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "CtsDevicePolicyManagerTestCases",
"options": [
{
"exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.LargeTest"
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsDevicePolicyManagerTestCases"
+ }
]
}
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/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 4c27a3c..8c454424 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -36,6 +36,7 @@
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 757267e5..bde0ef6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -262,6 +262,35 @@
}
@Test
+ public void testFindTaskAlias() {
+ final String targetActivity = "target.activity";
+ final String aliasActivity = "alias.activity";
+ final ComponentName target = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME,
+ targetActivity);
+ final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME,
+ aliasActivity);
+ final TaskRecord task = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
+ task.origActivity = alias;
+ task.realActivity = target;
+ new ActivityBuilder(mService).setComponent(target).setTask(task).setTargetActivity(
+ targetActivity).build();
+
+ // Using target activity to find task.
+ final ActivityRecord r1 = new ActivityBuilder(mService).setComponent(
+ target).setTargetActivity(targetActivity).build();
+ RootActivityContainer.FindTaskResult result = new RootActivityContainer.FindTaskResult();
+ mStack.findTaskLocked(r1, result);
+ assertThat(result.mRecord).isNotNull();
+
+ // Using alias activity to find task.
+ final ActivityRecord r2 = new ActivityBuilder(mService).setComponent(
+ alias).setTargetActivity(targetActivity).build();
+ result = new RootActivityContainer.FindTaskResult();
+ mStack.findTaskLocked(r2, result);
+ assertThat(result.mRecord).isNotNull();
+ }
+
+ @Test
public void testMoveStackToBackIncludingParent() {
final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
final ActivityStack stack1 = createStackForShouldBeVisibleTest(display,
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/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/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/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 8ab71fd..a27df47 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -1058,6 +1058,10 @@
Uninstall.packages(TestApp.A);
Install.single(TestApp.A1).commit();
+
+ // Block the RollbackManager to make extra sure it will not be
+ // able to enable the rollback in time.
+ rm.blockRollbackManager(TimeUnit.SECONDS.toMillis(1));
Install.single(TestApp.A2).setEnableRollback().commit();
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
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..42328ba 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -71,6 +71,7 @@
import static com.android.testutils.ConcurrentUtilsKt.await;
import static com.android.testutils.ConcurrentUtilsKt.durationOf;
+import static com.android.testutils.ExceptionUtils.ignoreExceptions;
import static com.android.testutils.HandlerUtilsKt.waitForIdleSerialExecutor;
import static com.android.testutils.MiscAssertsKt.assertContainsExactly;
import static com.android.testutils.MiscAssertsKt.assertEmpty;
@@ -159,6 +160,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,9 +206,8 @@
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.ExceptionUtils;
import com.android.testutils.HandlerUtilsKt;
-import com.android.testutils.ThrowingConsumer;
import org.junit.After;
import org.junit.Before;
@@ -227,7 +228,6 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -417,7 +417,7 @@
}
@Test
- public void testWaitForIdle() {
+ public void testWaitForIdle() throws Exception {
final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng.
// Tests that waitForIdle returns immediately if the service is already idle.
@@ -444,7 +444,7 @@
// This test has an inherent race condition in it, and cannot be enabled for continuous testing
// or presubmit tests. It is kept for manual runs and documentation purposes.
@Ignore
- public void verifyThatNotWaitingForIdleCausesRaceConditions() {
+ public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception {
// Bring up a network that we can use to send messages to ConnectivityService.
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -524,11 +524,11 @@
mNmValidationRedirectUrl = null;
}
- MockNetworkAgent(int transport) {
+ MockNetworkAgent(int transport) throws Exception {
this(transport, new LinkProperties());
}
- MockNetworkAgent(int transport, LinkProperties linkProperties) {
+ MockNetworkAgent(int transport, LinkProperties linkProperties) throws Exception {
final int type = transportToLegacyType(transport);
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
@@ -559,16 +559,12 @@
mNetworkMonitor = mock(INetworkMonitor.class);
final Answer validateAnswer = inv -> {
- new Thread(this::onValidationRequested).start();
+ new Thread(ignoreExceptions(this::onValidationRequested)).start();
return null;
};
- try {
- doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected(any(), any());
- doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt());
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
+ doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected(any(), any());
+ doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt());
final ArgumentCaptor<Network> nmNetworkCaptor = ArgumentCaptor.forClass(Network.class);
final ArgumentCaptor<INetworkMonitorCallbacks> nmCbCaptor =
@@ -623,35 +619,27 @@
assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId);
mNmCallbacks = nmCbCaptor.getValue();
- try {
- mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor);
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
+ mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor);
// Waits for the NetworkAgent to be registered, which includes the creation of the
// NetworkMonitor.
waitForIdle();
}
- private void onValidationRequested() {
- try {
- if (mNmProvNotificationRequested
- && ((mNmValidationResult & NETWORK_VALIDATION_RESULT_VALID) != 0)) {
- mNmCallbacks.hideProvisioningNotification();
- mNmProvNotificationRequested = false;
- }
+ private void onValidationRequested() throws Exception {
+ if (mNmProvNotificationRequested
+ && ((mNmValidationResult & NETWORK_VALIDATION_RESULT_VALID) != 0)) {
+ mNmCallbacks.hideProvisioningNotification();
+ mNmProvNotificationRequested = false;
+ }
- mNmCallbacks.notifyNetworkTested(
- mNmValidationResult, mNmValidationRedirectUrl);
+ mNmCallbacks.notifyNetworkTested(
+ mNmValidationResult, mNmValidationRedirectUrl);
- if (mNmValidationRedirectUrl != null) {
- mNmCallbacks.showProvisioningNotification(
- "test_provisioning_notif_action", "com.android.test.package");
- mNmProvNotificationRequested = true;
- }
- } catch (RemoteException e) {
- fail(e.getMessage());
+ if (mNmValidationRedirectUrl != null) {
+ mNmCallbacks.showProvisioningNotification(
+ "test_provisioning_notif_action", "com.android.test.package");
+ mNmProvNotificationRequested = true;
}
}
@@ -1249,18 +1237,13 @@
waitForIdle(TIMEOUT_MS);
}
- public void setUidRulesChanged(int uidRules) {
- try {
- mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
- } catch (RemoteException ignored) {
- }
+ public void setUidRulesChanged(int uidRules) throws RemoteException {
+ mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
}
- public void setRestrictBackgroundChanged(boolean restrictBackground) {
- try {
- mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
- } catch (RemoteException ignored) {
- }
+ public void setRestrictBackgroundChanged(boolean restrictBackground)
+ throws RemoteException {
+ mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
}
}
@@ -1815,12 +1798,9 @@
return mLastAvailableNetwork;
}
- CallbackInfo nextCallback(int timeoutMs) {
+ CallbackInfo nextCallback(int timeoutMs) throws InterruptedException {
CallbackInfo cb = null;
- try {
- cb = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- }
+ cb = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
if (cb == null) {
// LinkedBlockingQueue.poll() returns null if it timeouts.
fail("Did not receive callback after " + timeoutMs + "ms");
@@ -1828,7 +1808,8 @@
return cb;
}
- CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent, int timeoutMs) {
+ CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent, int timeoutMs)
+ throws Exception {
final Network expectedNetwork = (agent != null) ? agent.getNetwork() : null;
CallbackInfo expected = new CallbackInfo(state, expectedNetwork, 0);
CallbackInfo actual = nextCallback(timeoutMs);
@@ -1845,15 +1826,16 @@
return actual;
}
- CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent) {
+ CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent) throws Exception {
return expectCallback(state, agent, TEST_CALLBACK_TIMEOUT_MS);
}
- CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn) {
+ CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn) throws Exception {
return expectCallbackLike(fn, TEST_CALLBACK_TIMEOUT_MS);
}
- CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn, int timeoutMs) {
+ CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn, int timeoutMs)
+ throws Exception {
int timeLeft = timeoutMs;
while (timeLeft > 0) {
long start = SystemClock.elapsedRealtime();
@@ -1879,7 +1861,7 @@
// onCapabilitiesChanged callback.
// @param timeoutMs how long to wait for the callbacks.
void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
- boolean expectValidated, boolean expectBlocked, int timeoutMs) {
+ boolean expectValidated, boolean expectBlocked, int timeoutMs) throws Exception {
expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
if (expectSuspended) {
expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
@@ -1894,23 +1876,26 @@
}
// Expects the available callbacks (validated), plus onSuspended.
- void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
+ void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated)
+ throws Exception {
expectAvailableCallbacks(agent, true, expectValidated, false, TEST_CALLBACK_TIMEOUT_MS);
}
- void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
+ void expectAvailableCallbacksValidated(MockNetworkAgent agent)
+ throws Exception {
expectAvailableCallbacks(agent, false, true, false, TEST_CALLBACK_TIMEOUT_MS);
}
- void expectAvailableCallbacksValidatedAndBlocked(MockNetworkAgent agent) {
+ void expectAvailableCallbacksValidatedAndBlocked(MockNetworkAgent agent) throws Exception {
expectAvailableCallbacks(agent, false, true, true, TEST_CALLBACK_TIMEOUT_MS);
}
- void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
+ void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) throws Exception {
expectAvailableCallbacks(agent, false, false, false, TEST_CALLBACK_TIMEOUT_MS);
}
- void expectAvailableCallbacksUnvalidatedAndBlocked(MockNetworkAgent agent) {
+ void expectAvailableCallbacksUnvalidatedAndBlocked(MockNetworkAgent agent)
+ throws Exception {
expectAvailableCallbacks(agent, false, false, true, TEST_CALLBACK_TIMEOUT_MS);
}
@@ -1918,7 +1903,7 @@
// VALIDATED capability), plus another onCapabilitiesChanged which is identical to the
// one we just sent.
// TODO: this is likely a bug. Fix it and remove this method.
- void expectAvailableDoubleValidatedCallbacks(MockNetworkAgent agent) {
+ void expectAvailableDoubleValidatedCallbacks(MockNetworkAgent agent) throws Exception {
expectCallback(CallbackState.AVAILABLE, agent, TEST_CALLBACK_TIMEOUT_MS);
NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
expectCallback(CallbackState.LINK_PROPERTIES, agent, TEST_CALLBACK_TIMEOUT_MS);
@@ -1932,48 +1917,53 @@
// Expects the available callbacks where the onCapabilitiesChanged must not have validated,
// then expects another onCapabilitiesChanged that has the validated bit set. This is used
// when a network connects and satisfies a callback, and then immediately validates.
- void expectAvailableThenValidatedCallbacks(MockNetworkAgent agent) {
+ void expectAvailableThenValidatedCallbacks(MockNetworkAgent agent) throws Exception {
expectAvailableCallbacksUnvalidated(agent);
expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
}
- NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
+ NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent)
+ throws Exception {
return expectCapabilitiesWith(capability, agent, TEST_CALLBACK_TIMEOUT_MS);
}
NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent,
- int timeoutMs) {
+ int timeoutMs) throws Exception {
CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
assertTrue(nc.hasCapability(capability));
return nc;
}
- NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
+ NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent)
+ throws Exception {
return expectCapabilitiesWithout(capability, agent, TEST_CALLBACK_TIMEOUT_MS);
}
NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent,
- int timeoutMs) {
+ int timeoutMs) throws Exception {
CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
assertFalse(nc.hasCapability(capability));
return nc;
}
- void expectCapabilitiesLike(Predicate<NetworkCapabilities> fn, MockNetworkAgent agent) {
+ void expectCapabilitiesLike(Predicate<NetworkCapabilities> fn, MockNetworkAgent agent)
+ throws Exception {
CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
assertTrue("Received capabilities don't match expectations : " + cbi.arg,
fn.test((NetworkCapabilities) cbi.arg));
}
- void expectLinkPropertiesLike(Predicate<LinkProperties> fn, MockNetworkAgent agent) {
+ void expectLinkPropertiesLike(Predicate<LinkProperties> fn, MockNetworkAgent agent)
+ throws Exception {
CallbackInfo cbi = expectCallback(CallbackState.LINK_PROPERTIES, agent);
assertTrue("Received LinkProperties don't match expectations : " + cbi.arg,
fn.test((LinkProperties) cbi.arg));
}
- void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent) {
+ void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent)
+ throws Exception {
CallbackInfo cbi = expectCallback(CallbackState.BLOCKED_STATUS, agent);
boolean actualBlocked = (boolean) cbi.arg;
assertEquals(expectBlocked, actualBlocked);
@@ -2085,7 +2075,7 @@
}
@Test
- public void testMultipleLingering() {
+ public void testMultipleLingering() throws Exception {
// This test would be flaky with the default 120ms timer: that is short enough that
// lingered networks are torn down before assertions can be run. We don't want to mock the
// lingering timer to keep the WakeupMessage logic realistic: this has already proven useful
@@ -2340,7 +2330,7 @@
}
@Test
- public void testNetworkGoesIntoBackgroundAfterLinger() {
+ public void testNetworkGoesIntoBackgroundAfterLinger() throws Exception {
setAlwaysOnNetworks(true);
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities()
@@ -2385,7 +2375,7 @@
}
@Test
- public void testExplicitlySelected() {
+ public void testExplicitlySelected() throws Exception {
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
.build();
@@ -2666,7 +2656,7 @@
}
@Test
- public void testPartialConnectivity() {
+ public void testPartialConnectivity() throws Exception {
// Register network callback.
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
@@ -2699,11 +2689,7 @@
// If user accepts partial connectivity network,
// NetworkMonitor#setAcceptPartialConnectivity() should be called too.
waitForIdle();
- try {
- verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
+ verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
// Need a trigger point to let NetworkMonitor tell ConnectivityService that network is
// validated.
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
@@ -2740,11 +2726,7 @@
// NetworkMonitor#setAcceptPartialConnectivity() will be called in
// ConnectivityService#updateNetworkInfo().
waitForIdle();
- try {
- verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
+ verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
@@ -2768,11 +2750,7 @@
// NetworkMonitor#setAcceptPartialConnectivity() will be called in
// ConnectivityService#updateNetworkInfo().
waitForIdle();
- try {
- verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
+ verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
// TODO: If the user accepted partial connectivity, we shouldn't switch to wifi until
@@ -2789,7 +2767,7 @@
}
@Test
- public void testCaptivePortalOnPartialConnectivity() throws RemoteException {
+ public void testCaptivePortalOnPartialConnectivity() throws Exception {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
@@ -2838,7 +2816,7 @@
}
@Test
- public void testCaptivePortal() {
+ public void testCaptivePortal() throws Exception {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
@@ -2890,7 +2868,7 @@
}
@Test
- public void testCaptivePortalApp() throws RemoteException {
+ public void testCaptivePortalApp() throws Exception {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
@@ -2948,7 +2926,7 @@
}
@Test
- public void testAvoidOrIgnoreCaptivePortals() {
+ public void testAvoidOrIgnoreCaptivePortals() throws Exception {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
@@ -2988,7 +2966,7 @@
* does work.
*/
@Test
- public void testNetworkSpecifier() {
+ public void testNetworkSpecifier() throws Exception {
// A NetworkSpecifier subclass that matches all networks but must not be visible to apps.
class ConfidentialMatchAllNetworkSpecifier extends NetworkSpecifier implements
Parcelable {
@@ -3128,24 +3106,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 +3126,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,14 +3151,11 @@
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
- public void testNetworkSpecifierUidSpoofSecurityException() {
+ public void testNetworkSpecifierUidSpoofSecurityException() throws Exception {
class UidAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
@Override
public boolean satisfiedBy(NetworkSpecifier other) {
@@ -3213,12 +3180,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 +3194,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
@@ -3820,7 +3768,7 @@
* time-out period expires.
*/
@Test
- public void testSatisfiedNetworkRequestDoesNotTriggerOnUnavailable() {
+ public void testSatisfiedNetworkRequestDoesNotTriggerOnUnavailable() throws Exception {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
@@ -3840,7 +3788,7 @@
* not trigger onUnavailable() once the time-out period expires.
*/
@Test
- public void testSatisfiedThenLostNetworkRequestDoesNotTriggerOnUnavailable() {
+ public void testSatisfiedThenLostNetworkRequestDoesNotTriggerOnUnavailable() throws Exception {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
@@ -3863,7 +3811,7 @@
* (somehow) satisfied - the callback isn't called later.
*/
@Test
- public void testTimedoutNetworkRequest() {
+ public void testTimedoutNetworkRequest() throws Exception {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
@@ -3884,7 +3832,7 @@
* trigger the callback.
*/
@Test
- public void testNoCallbackAfterUnregisteredNetworkRequest() {
+ public void testNoCallbackAfterUnregisteredNetworkRequest() throws Exception {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
@@ -3976,7 +3924,7 @@
private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
- public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
+ public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR }
private class CallbackValue {
public CallbackType callbackType;
@@ -4024,25 +3972,19 @@
mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
}
- private void expectCallback(CallbackValue callbackValue) {
- try {
- assertEquals(
- callbackValue,
- mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } catch (InterruptedException e) {
- fail(callbackValue.callbackType + " callback not seen after " + TIMEOUT_MS + " ms");
- }
+ private void expectCallback(CallbackValue callbackValue) throws InterruptedException {
+ assertEquals(callbackValue, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
- public void expectStarted() {
+ public void expectStarted() throws Exception {
expectCallback(new CallbackValue(CallbackType.ON_STARTED));
}
- public void expectStopped() {
+ public void expectStopped() throws Exception {
expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
}
- public void expectError(int error) {
+ public void expectError(int error) throws Exception {
expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
}
}
@@ -4103,25 +4045,20 @@
mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
}
- private void expectCallback(CallbackValue callbackValue) {
- try {
- assertEquals(
- callbackValue,
- mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } catch (InterruptedException e) {
- fail(callbackValue.callbackType + " callback not seen after " + TIMEOUT_MS + " ms");
- }
+ private void expectCallback(CallbackValue callbackValue) throws InterruptedException {
+ assertEquals(callbackValue, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
}
- public void expectStarted() {
+ public void expectStarted() throws InterruptedException {
expectCallback(new CallbackValue(CallbackType.ON_STARTED));
}
- public void expectStopped() {
+ public void expectStopped() throws InterruptedException {
expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
}
- public void expectError(int error) {
+ public void expectError(int error) throws InterruptedException {
expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
}
@@ -4132,7 +4069,7 @@
}
}
- private Network connectKeepaliveNetwork(LinkProperties lp) {
+ private Network connectKeepaliveNetwork(LinkProperties lp) throws Exception {
// Ensure the network is disconnected before we do anything.
if (mWiFiNetworkAgent != null) {
assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()));
@@ -4266,7 +4203,8 @@
}
// Helper method to prepare the executor and run test
- private void runTestWithSerialExecutors(ThrowingConsumer<Executor> functor) throws Exception {
+ private void runTestWithSerialExecutors(ExceptionUtils.ThrowingConsumer<Executor> functor)
+ throws Exception {
final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor();
final Executor executorInline = (Runnable r) -> r.run();
functor.accept(executorSingleThread);
@@ -4584,7 +4522,7 @@
private static boolean isUdpPortInUse(int port) {
try (DatagramSocket ignored = new DatagramSocket(port)) {
return false;
- } catch (IOException ignored) {
+ } catch (IOException alreadyInUse) {
return true;
}
}
@@ -4596,23 +4534,19 @@
}
private static class TestNetworkPinner extends NetworkPinner {
- public static boolean awaitPin(int timeoutMs) {
+ public static boolean awaitPin(int timeoutMs) throws InterruptedException {
synchronized(sLock) {
if (sNetwork == null) {
- try {
- sLock.wait(timeoutMs);
- } catch (InterruptedException e) {}
+ sLock.wait(timeoutMs);
}
return sNetwork != null;
}
}
- public static boolean awaitUnpin(int timeoutMs) {
+ public static boolean awaitUnpin(int timeoutMs) throws InterruptedException {
synchronized(sLock) {
if (sNetwork != null) {
- try {
- sLock.wait(timeoutMs);
- } catch (InterruptedException e) {}
+ sLock.wait(timeoutMs);
}
return sNetwork == null;
}
@@ -4635,7 +4569,7 @@
}
@Test
- public void testNetworkPinner() {
+ public void testNetworkPinner() throws Exception {
NetworkRequest wifiRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_WIFI)
.build();
@@ -4730,25 +4664,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) {
@@ -4792,7 +4721,7 @@
}
@Test
- public void testNetworkInfoOfTypeNone() {
+ public void testNetworkInfoOfTypeNone() throws Exception {
ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1);
verifyNoNetwork();
@@ -4853,7 +4782,7 @@
}
@Test
- public void testLinkPropertiesEnsuresDirectlyConnectedRoutes() {
+ public void testLinkPropertiesEnsuresDirectlyConnectedRoutes() throws Exception {
final NetworkRequest networkRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
@@ -4916,11 +4845,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 +4855,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);
}
@@ -5282,7 +5190,7 @@
}
@Test
- public void testVpnNetworkActive() {
+ public void testVpnNetworkActive() throws Exception {
final int uid = Process.myUid();
final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
@@ -5394,7 +5302,7 @@
}
@Test
- public void testVpnWithoutInternet() {
+ public void testVpnWithoutInternet() throws Exception {
final int uid = Process.myUid();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
@@ -5424,7 +5332,7 @@
}
@Test
- public void testVpnWithInternet() {
+ public void testVpnWithInternet() throws Exception {
final int uid = Process.myUid();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
@@ -5507,7 +5415,7 @@
}
@Test
- public void testVpnSetUnderlyingNetworks() {
+ public void testVpnSetUnderlyingNetworks() throws Exception {
final int uid = Process.myUid();
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
@@ -5605,7 +5513,7 @@
}
@Test
- public void testNullUnderlyingNetworks() {
+ public void testNullUnderlyingNetworks() throws Exception {
final int uid = Process.myUid();
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
@@ -5669,7 +5577,7 @@
}
@Test
- public void testIsActiveNetworkMeteredOverWifi() {
+ public void testIsActiveNetworkMeteredOverWifi() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -5681,7 +5589,7 @@
}
@Test
- public void testIsActiveNetworkMeteredOverCell() {
+ public void testIsActiveNetworkMeteredOverCell() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -5693,7 +5601,7 @@
}
@Test
- public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() {
+ public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -5747,7 +5655,7 @@
}
@Test
- public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() {
+ public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -5818,7 +5726,7 @@
}
@Test
- public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() {
+ public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -5865,7 +5773,7 @@
}
@Test
- public void testNetworkBlockedStatus() {
+ public void testNetworkBlockedStatus() throws Exception {
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
final NetworkRequest cellRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
@@ -5916,7 +5824,7 @@
}
@Test
- public void testNetworkBlockedStatusBeforeAndAfterConnect() {
+ public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception {
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
@@ -5985,7 +5893,7 @@
}
@Test
- public void testStackedLinkProperties() throws UnknownHostException, RemoteException {
+ public void testStackedLinkProperties() throws Exception {
final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64");
final String kNat64PrefixString = "2001:db8:64:64:64:64::";
@@ -6148,7 +6056,7 @@
}
@Test
- public void testDataActivityTracking() throws RemoteException {
+ public void testDataActivityTracking() throws Exception {
final TestNetworkCallback networkCallback = new TestNetworkCallback();
final NetworkRequest networkRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET)
@@ -6223,21 +6131,17 @@
mCm.unregisterNetworkCallback(networkCallback);
}
- private void verifyTcpBufferSizeChange(String tcpBufferSizes) {
+ private void verifyTcpBufferSizeChange(String tcpBufferSizes) throws Exception {
String[] values = tcpBufferSizes.split(",");
String rmemValues = String.join(" ", values[0], values[1], values[2]);
String wmemValues = String.join(" ", values[3], values[4], values[5]);
waitForIdle();
- try {
- verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues);
- } catch (RemoteException e) {
- fail("mMockNetd should never throw RemoteException");
- }
+ verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues);
reset(mMockNetd);
}
@Test
- public void testTcpBufferReset() {
+ public void testTcpBufferReset() throws Exception {
final String testTcpBufferSizes = "1,2,3,4,5,6";
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -6254,7 +6158,7 @@
}
@Test
- public void testGetGlobalProxyForNetwork() {
+ public void testGetGlobalProxyForNetwork() throws Exception {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
final Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
@@ -6263,7 +6167,7 @@
}
@Test
- public void testGetProxyForActiveNetwork() {
+ public void testGetProxyForActiveNetwork() throws Exception {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
@@ -6280,7 +6184,7 @@
}
@Test
- public void testGetProxyForVPN() {
+ public void testGetProxyForVPN() throws Exception {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
// Set up a WiFi network with no proxy
@@ -6466,7 +6370,7 @@
private MockNetworkAgent establishVpn(LinkProperties lp, int establishingUid,
- Set<UidRange> vpnRange) {
+ Set<UidRange> vpnRange) throws Exception {
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN, lp);
vpnNetworkAgent.getNetworkCapabilities().setEstablishingVpnAppUid(establishingUid);
mMockVpn.setNetworkAgent(vpnNetworkAgent);
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;