upload android base code part6

This commit is contained in:
August 2018-08-08 17:48:24 +08:00
parent 421e214c7d
commit 4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions

View file

@ -0,0 +1,35 @@
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := crashcollector
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp/crashcollector
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_COMPATIBILITY_SUITE := device-tests
include $(BUILD_JAVA_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := crashcollector
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp/crashcollector
LOCAL_SRC_FILES := crashcollector
include $(BUILD_PREBUILT)

View file

@ -0,0 +1,19 @@
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
base=/data/local/tmp/crashcollector
export CLASSPATH=$base/crashcollector.jar
exec app_process $base android.test.crashcollector.Collector $*

View file

@ -0,0 +1,217 @@
/*
* Copyright 2016, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.test.crashcollector;
import android.app.IActivityController;
import android.app.IActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.util.Log;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
/**
* Main class for the crash collector that installs an activity controller to monitor app errors
*/
public class Collector {
private static final String LOG_TAG = "CrashCollector";
private static final long CHECK_AM_INTERVAL_MS = 5 * 1000;
private static final long MAX_CHECK_AM_TIMEOUT_MS = 30 * 1000;
private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
private static final File TOMBSTONES_PATH = new File("/data/tombstones");
private HashSet<String> mTombstones = null;
/**
* Command-line entry point.
*
* @param args The command-line arguments
*/
public static void main(String[] args) {
// Set the process name showing in "ps" or "top"
Process.setArgV0("android.test.crashcollector");
int resultCode = (new Collector()).run(args);
System.exit(resultCode);
}
/**
* Command execution entry point
* @param args
* @return
* @throws RemoteException
*/
public int run(String[] args) {
// recipient for activity manager death so that command can survive runtime restart
final IBinder.DeathRecipient death = new DeathRecipient() {
@Override
public void binderDied() {
synchronized (this) {
notifyAll();
}
}
};
IBinder am = blockUntilSystemRunning(MAX_CHECK_AM_TIMEOUT_MS);
if (am == null) {
print("FATAL: Cannot get activity manager, is system running?");
return -1;
}
IActivityController controller = new CrashCollector();
do {
try {
// set activity controller
IActivityManager iam = IActivityManager.Stub.asInterface(am);
iam.setActivityController(controller, false);
// register death recipient for activity manager
am.linkToDeath(death, 0);
} catch (RemoteException re) {
print("FATAL: cannot set activity controller, is system running?");
re.printStackTrace();
return -1;
}
// monitor runtime restart (crash/kill of system server)
synchronized (death) {
while (am.isBinderAlive()) {
try {
Log.d(LOG_TAG, "Monitoring death of system server.");
death.wait();
} catch (InterruptedException e) {
// ignore
}
}
Log.w(LOG_TAG, "Detected crash of system server.");
am = blockUntilSystemRunning(MAX_CHECK_AM_TIMEOUT_MS);
}
} while (true);
// for now running indefinitely, until a better mechanism is found to signal shutdown
}
private void print(String line) {
System.err.println(String.format("%s %s", TIME_FORMAT.format(new Date()), line));
}
/**
* Blocks until system server is running, or timeout has reached
* @param timeout
* @return
*/
private IBinder blockUntilSystemRunning(long timeout) {
// waiting for activity manager to come back
long start = SystemClock.uptimeMillis();
IBinder am = null;
while (SystemClock.uptimeMillis() - start < MAX_CHECK_AM_TIMEOUT_MS) {
am = ServiceManager.checkService(Context.ACTIVITY_SERVICE);
if (am != null) {
break;
} else {
Log.d(LOG_TAG, "activity manager not ready yet, continue waiting.");
try {
Thread.sleep(CHECK_AM_INTERVAL_MS);
} catch (InterruptedException e) {
// break out of current loop upon interruption
break;
}
}
}
return am;
}
private boolean checkNativeCrashes() {
String[] tombstones = TOMBSTONES_PATH.list();
// shortcut path for usually empty directory, so we don't waste even
// more objects
if ((tombstones == null) || (tombstones.length == 0)) {
mTombstones = null;
return false;
}
// use set logic to look for new files
HashSet<String> newStones = new HashSet<String>();
for (String x : tombstones) {
newStones.add(x);
}
boolean result = (mTombstones == null) || !mTombstones.containsAll(newStones);
// keep the new list for the next time
mTombstones = newStones;
return result;
}
private class CrashCollector extends IActivityController.Stub {
@Override
public boolean activityStarting(Intent intent, String pkg) throws RemoteException {
// check native crashes when we have a chance
if (checkNativeCrashes()) {
print("NATIVE: new tombstones");
}
return true;
}
@Override
public boolean activityResuming(String pkg) throws RemoteException {
// check native crashes when we have a chance
if (checkNativeCrashes()) {
print("NATIVE: new tombstones");
}
return true;
}
@Override
public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
long timeMillis, String stackTrace) throws RemoteException {
if (processName == null) {
print("CRASH: null process name, assuming system");
} else {
print("CRASH: " + processName);
}
return false;
}
@Override
public int appEarlyNotResponding(String processName, int pid, String annotation)
throws RemoteException {
// ignore
return 0;
}
@Override
public int appNotResponding(String processName, int pid, String processStats)
throws RemoteException {
print("ANR: " + processName);
return -1;
}
@Override
public int systemNotResponding(String msg) throws RemoteException {
print("WATCHDOG: " + msg);
return -1;
}
}
}

View file

@ -0,0 +1,27 @@
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
# -----------------------------------------------------------------------
# The static library that platform/app helpers can link against
include $(CLEAR_VARS)
LOCAL_MODULE := dpad-util
LOCAL_JAVA_LIBRARIES := ub-uiautomator android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
include $(BUILD_STATIC_JAVA_LIBRARY)

View file

@ -0,0 +1,200 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.platform.test.utils;
import android.app.Instrumentation;
import android.os.SystemClock;
import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.EventCondition;
import android.support.test.uiautomator.UiDevice;
import android.util.Log;
import android.view.KeyEvent;
import java.io.IOException;
public class DPadUtil {
private static final String TAG = DPadUtil.class.getSimpleName();
private static final long DPAD_DEFAULT_WAIT_TIME_MS = 1000; // 1 sec
private UiDevice mDevice;
public DPadUtil(Instrumentation instrumentation) {
mDevice = UiDevice.getInstance(instrumentation);
}
public DPadUtil(UiDevice uiDevice) {
mDevice = uiDevice;
}
public void setUiDevice(UiDevice uiDevice) {
mDevice = uiDevice;
}
public boolean pressDPad(Direction direction) {
return pressDPad(direction, 1, DPAD_DEFAULT_WAIT_TIME_MS);
}
public void pressDPad(Direction direction, long repeat) {
pressDPad(direction, repeat, DPAD_DEFAULT_WAIT_TIME_MS);
}
/**
* Presses DPad button of the same direction for the count times.
* It sleeps between each press for DPAD_DEFAULT_WAIT_TIME_MS.
*
* @param direction the direction of the button to press.
* @param repeat the number of times to press the button.
* @param timeout the timeout for the wait.
* @return true if the last key simulation is successful, else return false
*/
public boolean pressDPad(Direction direction, long repeat, long timeout) {
int iteration = 0;
boolean result = false;
while (iteration++ < repeat) {
switch (direction) {
case LEFT:
result = mDevice.pressDPadLeft();
break;
case RIGHT:
result = mDevice.pressDPadRight();
break;
case UP:
result = mDevice.pressDPadUp();
break;
case DOWN:
result = mDevice.pressDPadDown();
break;
}
SystemClock.sleep(timeout);
}
return result;
}
public boolean pressDPadLeft() {
return pressKeyCodeAndWait(KeyEvent.KEYCODE_DPAD_LEFT);
}
public boolean pressDPadRight() {
return pressKeyCodeAndWait(KeyEvent.KEYCODE_DPAD_RIGHT);
}
public boolean pressDPadUp() {
return pressKeyCodeAndWait(KeyEvent.KEYCODE_DPAD_UP);
}
public boolean pressDPadDown() {
return pressKeyCodeAndWait(KeyEvent.KEYCODE_DPAD_DOWN);
}
public boolean pressDPadCenter() {
return pressKeyCodeAndWait(KeyEvent.KEYCODE_DPAD_CENTER);
}
public boolean pressEnter() {
return pressKeyCodeAndWait(KeyEvent.KEYCODE_ENTER);
}
public boolean pressPipKey() {
return pressKeyCodeAndWait(KeyEvent.KEYCODE_WINDOW);
}
public boolean pressSearch() {
return pressKeyCodeAndWait(KeyEvent.KEYCODE_SEARCH);
}
public boolean pressKeyCode(int keyCode) {
return pressKeyCodeAndWait(keyCode);
}
public boolean pressKeyCodeAndWait(int keyCode) {
boolean retVal = mDevice.pressKeyCode(keyCode);
// Dpad key presses will cause some UI change to occur.
// Wait for the accessibility event stream to become idle.
mDevice.waitForIdle();
return retVal;
}
public boolean pressHome() {
return mDevice.pressHome();
}
public boolean pressBack() {
return mDevice.pressBack();
}
public boolean longPressKeyCode(int keyCode) {
try {
mDevice.executeShellCommand(String.format("input keyevent --longpress %d", keyCode));
mDevice.waitForIdle();
return true;
} catch (IOException e) {
// Ignore
Log.w(TAG, String.format("Failed to long press the key code: %d", keyCode));
return false;
}
}
/**
* Press the key code, and waits for the given condition to become true.
* @param keyCode
* @param condition
* @param longpress
* @param timeout
* @param <R>
* @return
*/
public <R> R pressKeyCodeAndWait(int keyCode, EventCondition<R> condition, boolean longpress,
long timeout) {
return mDevice.performActionAndWait(new KeyEventRunnable(keyCode, longpress), condition,
timeout);
}
public <R> R pressKeyCodeAndWait(int keyCode, EventCondition<R> condition, long timeout) {
return pressKeyCodeAndWait(keyCode, condition, false, timeout);
}
public <R> R pressDPadCenterAndWait(EventCondition<R> condition, long timeout) {
return mDevice.performActionAndWait(new KeyEventRunnable(KeyEvent.KEYCODE_DPAD_CENTER),
condition, timeout);
}
public <R> R pressEnterAndWait(EventCondition<R> condition, long timeout) {
return mDevice.performActionAndWait(new KeyEventRunnable(KeyEvent.KEYCODE_ENTER),
condition, timeout);
}
private class KeyEventRunnable implements Runnable {
private int mKeyCode;
private boolean mLongPress = false;
public KeyEventRunnable(int keyCode) {
mKeyCode = keyCode;
}
public KeyEventRunnable(int keyCode, boolean longpress) {
mKeyCode = keyCode;
mLongPress = longpress;
}
@Override
public void run() {
if (mLongPress) {
longPressKeyCode(mKeyCode);
} else {
pressKeyCode(mKeyCode);
}
}
}
}

View file

@ -0,0 +1,36 @@
#
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := permission-utils-lib
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_SRC_FILES := $(call all-java-files-under, src)
include $(BUILD_STATIC_JAVA_LIBRARY)
######################################
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := PermissionUtils
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright (c) 2015 Google Inc.
*
* 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.permissionutils">
<uses-sdk android:minSdkVersion="23"
android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.GRANT_REVOKE_PERMISSIONS" />
<uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name=".PermissionInstrumentation"
android:targetPackage="com.android.permissionutils"
android:label="Permission Utils">
</instrumentation>
</manifest>

View file

@ -0,0 +1,96 @@
/*
* Copyright (C) 2015 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.permissionutils;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* A utility to dump or grant all revoked runtime permissions
*/
public class GrantPermissionUtil {
private static final String LOG_TAG = GrantPermissionUtil.class.getSimpleName();
public static void grantAllPermissions (Context context) {
PackageManager pm = context.getPackageManager();
for (PackageInfo pkgInfo : getPackageInfos(context)) {
List<String> missingPermissions = getMissingPermissions(context, pkgInfo);
if (!missingPermissions.isEmpty()) {
for (String permission : missingPermissions) {
pm.grantRuntimePermission(pkgInfo.packageName, permission, UserHandle.OWNER);
}
}
}
}
public static void dumpMissingPermissions (Context context) {
for (PackageInfo pkgInfo : getPackageInfos(context)) {
List<String> missingPermissions = getMissingPermissions(context, pkgInfo);
if (!missingPermissions.isEmpty()) {
Log.e(LOG_TAG, String.format("Missing permissions for %s", pkgInfo.packageName));
for (String permission : missingPermissions) {
Log.e(LOG_TAG, " " + permission);
}
}
}
}
private static List<PackageInfo> getPackageInfos(Context context) {
return context.getPackageManager().getInstalledPackages(PackageManager.GET_PERMISSIONS);
}
private static List<String> getMissingPermissions(Context context, PackageInfo info) {
// No requested permissions
if (info.requestedPermissions == null) {
return new ArrayList<>();
}
List<String> result = new ArrayList<>();
PackageManager pm = context.getPackageManager();
// Iterate through requested permissions for denied ones
for (String permission : info.requestedPermissions) {
PermissionInfo pi = null;
try {
pi = pm.getPermissionInfo(permission, 0);
} catch (NameNotFoundException nnfe) {
// ignore
}
if (pi == null) {
continue;
}
if (!isRuntime(pi)) {
continue;
}
int flag = pm.checkPermission(permission, info.packageName);
if (flag == PackageManager.PERMISSION_DENIED) {
result.add(permission);
}
}
return result;
}
private static boolean isRuntime(PermissionInfo pi) {
return (pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS;
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (C) 2015 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.permissionutils;
import android.app.Activity;
import android.app.Instrumentation;
import android.os.Bundle;
import com.android.permissionutils.GrantPermissionUtil;
/**
* A utility to dump or grant all revoked runtime permissions
*/
public class PermissionInstrumentation extends Instrumentation {
private static final String PARAM_COMMAND = "command";
private static final String COMMAND_DUMP = "dump";
private static final String COMMAND_GRANTALL = "grant-all";
@Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
String command = arguments.getString(PARAM_COMMAND);
if (command == null) {
throw new IllegalArgumentException("missing command parameter");
}
if (COMMAND_DUMP.equals(command)) {
GrantPermissionUtil.dumpMissingPermissions(getContext());
} else if (COMMAND_GRANTALL.equals(command)) {
GrantPermissionUtil.grantAllPermissions(getContext());
} else {
throw new IllegalArgumentException(
String.format("unrecognized command \"%s\"", command));
}
finish(Activity.RESULT_OK, new Bundle());
}
}

View file

@ -0,0 +1,27 @@
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := WifiStrengthScannerUtil
LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := 23
include $(BUILD_PACKAGE)

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright (c) 2016 Google Inc.
*
* 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.util.wifistrengthscanner">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-sdk android:minSdkVersion="23"
android:targetSdkVersion="23" />
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name=".WifiStrengthScannerInstrumentation"
android:targetPackage="com.android.test.util.wifistrengthscanner"
android:label="Dismiss Dialog Util">
</instrumentation>
</manifest>

View file

@ -0,0 +1,195 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test.util.wifistrengthscanner;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class WifiStrengthScannerInstrumentation extends Instrumentation {
private static final String TAG = WifiStrengthScannerInstrumentation.class.getCanonicalName();
private final static String SD_CARD_PATH =
Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
private final int NUMBER_OF_WIFI_LEVELS = 101;
private final int INVALID_RSSI = -127;
private Bundle mArguments;
private CountDownLatch mLatch;
private boolean scanReceived;
@Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
mArguments = arguments;
start();
}
@Override
public void onStart() {
super.onStart();
try {
mLatch = new CountDownLatch(1);
getContext().registerReceiver(new WifiScanReceiver(),
new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
WifiManager wifiManager =
(WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
scanReceived = false;
wifiManager.startScan();
mLatch.await(10000, TimeUnit.MILLISECONDS);
if (!scanReceived) {
sendFailureStatus("no_scan_received");
finish(Activity.RESULT_CANCELED, new Bundle());
return;
}
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
Bundle bundle = new Bundle();
bundle.putString("suplicant_state", wifiInfo.getSupplicantState().name());
String bssid = wifiInfo.getBSSID();
bundle.putString("bssid", bssid);
// zero counts as a level, so the max level is one less that the number of levels.
bundle.putInt("wifi_max_level", NUMBER_OF_WIFI_LEVELS - 1);
bundle.putInt("wifi_info_wifi_level",
WifiManager.calculateSignalLevel(wifiInfo.getRssi(), NUMBER_OF_WIFI_LEVELS));
bundle.putInt("wifi_info_rssi", wifiInfo.getRssi());
bundle.putInt("wifi_info_frequency", wifiInfo.getFrequency());
ScanResult result = getScanResult(wifiManager, bssid);
if (result != null) {
bundle.putInt("scan_result_wifi_level", wifiManager.calculateSignalLevel(result
.level, NUMBER_OF_WIFI_LEVELS));
bundle.putInt("scan_result_rssi", result.level);
bundle.putInt("scan_result_frequency", result.frequency);
}
int dumpsysRssi = getRssiFromDumpsys(bssid);
bundle.putInt("dumpsys_rssi", dumpsysRssi);
bundle.putInt("dumpsys_wifi_level",
WifiManager.calculateSignalLevel(dumpsysRssi, NUMBER_OF_WIFI_LEVELS));
sendStatus(Activity.RESULT_OK, bundle);
finish(Activity.RESULT_OK, new Bundle());
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
sendFailureStatus("io_exception");
finish(Activity.RESULT_CANCELED, new Bundle());
} catch (InterruptedException e) {
Log.e(TAG, Log.getStackTraceString(e));
sendFailureStatus("interrupted_exception");
finish(Activity.RESULT_CANCELED, new Bundle());
}
}
private ScanResult getScanResult(WifiManager wifiManager, String bssid) {
List<ScanResult> scanResults = wifiManager.getScanResults();
for (ScanResult scanResult : scanResults) {
if (scanResult.BSSID.equals(bssid)) {
return scanResult;
}
}
return null;
}
private void sendFailureStatus(String update) {
Bundle result = new Bundle();
result.putString("wifi_strength_scanner_failure", update);
sendStatus(Activity.RESULT_CANCELED, result);
}
private Integer getRssiFromDumpsys(String bssid) throws IOException, InterruptedException {
List<String> dumpsysLines = getDumpsysWifiLastScanResults();
for (int i = 2; i < dumpsysLines.size(); i++) {
String line = dumpsysLines.get(i);
if (line != null && line.contains(bssid)) {
String[] tokens = line.trim().split("\\s\\s+");
return Integer.parseInt(tokens[2]);
}
}
return INVALID_RSSI;
}
private List<String> getDumpsysWifiLastScanResults() throws IOException, InterruptedException {
String dumpsysWifi = executeCommand("dumpsys wifi");
String[] lines = dumpsysWifi.split("\n");
List<String> scanResults = new ArrayList<>();
boolean scansStarted = false;
for (String line : lines) {
if (line.startsWith("Latest scan results:")) {
scansStarted = true;
}
if (scansStarted) {
if ("".equals(line.trim())) {
break;
}
scanResults.add(line);
}
}
return scanResults;
}
private String executeCommand(String cmd) throws IOException, InterruptedException {
StringBuilder result = new StringBuilder();
try {
ParcelFileDescriptor pfd = getUiAutomation().executeShellCommand(cmd);
byte[] buf = new byte[1024];
int bytesRead;
FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
while ((bytesRead = fis.read(buf)) != -1) {
result.append(new String(buf, 0, bytesRead));
}
fis.close();
} catch (IOException e) {
throw new IOException(String.format("Fails to execute command: %s ", cmd), e);
}
return result.toString();
}
private class WifiScanReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "scan results received.");
scanReceived = true;
mLatch.countDown();
}
}
}