blob: ec39f80ac471984c815705e0d2e575cd5731c51d [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
/**
* This class provides the cellular signal strength using the APIs provided by Android. This class
* is thread safe.
*/
@JNINamespace("net::android")
public class AndroidCellularSignalStrength {
// {@link mSignalLevel} is set to volatile since may be accessed across threads.
private volatile int mSignalLevel = CellularSignalStrengthError.ERROR_NOT_SUPPORTED;
private static final AndroidCellularSignalStrength sInstance =
new AndroidCellularSignalStrength();
/**
* This class listens to the changes in the cellular signal strength level and updates {@link
* mSignalLevel}. {@link CellStateListener} registers as a signal strength observer only if the
* application has running activities.
*/
private class CellStateListener
extends PhoneStateListener implements ApplicationStatus.ApplicationStateListener {
private final TelephonyManager mTelephonyManager;
CellStateListener() {
ThreadUtils.assertOnBackgroundThread();
mTelephonyManager =
(TelephonyManager) ContextUtils.getApplicationContext().getSystemService(
Context.TELEPHONY_SERVICE);
if (mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY) return;
ApplicationStatus.registerApplicationStateListener(this);
onApplicationStateChange(ApplicationStatus.getStateForApplication());
}
private void register() {
mTelephonyManager.listen(this, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
private void unregister() {
mSignalLevel = CellularSignalStrengthError.ERROR_NOT_SUPPORTED;
mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
}
@Override
@TargetApi(Build.VERSION_CODES.M)
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
if (ApplicationStatus.getStateForApplication()
!= ApplicationState.HAS_RUNNING_ACTIVITIES) {
return;
}
try {
mSignalLevel = signalStrength.getLevel();
} catch (SecurityException e) {
// Catch any exceptions thrown due to unavailability of permissions on certain
// Android devices. See https://crbug.com/820564 for details.
mSignalLevel = CellularSignalStrengthError.ERROR_NOT_SUPPORTED;
assert false;
}
}
// ApplicationStatus.ApplicationStateListener
@Override
public void onApplicationStateChange(int newState) {
if (newState == ApplicationState.HAS_RUNNING_ACTIVITIES) {
register();
} else if (newState == ApplicationState.HAS_PAUSED_ACTIVITIES) {
unregister();
}
}
}
private AndroidCellularSignalStrength() {
// {@link android.telephony.SignalStrength#getLevel} is only available on API Level
// {@link Build.VERSION_CODES#M} and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
HandlerThread handlerThread = new HandlerThread("AndroidCellularSignalStrength");
handlerThread.start();
new Handler(handlerThread.getLooper()).post(new Runnable() {
@Override
public void run() {
new CellStateListener();
}
});
}
/**
* @return the signal strength level (between 0 and 4, both inclusive) for the currently
* registered cellular network with lower value indicating lower signal strength. Returns
* {@link CellularSignalStrengthError#ERROR_NOT_SUPPORTED} if the signal strength level is
* unavailable.
*/
@TargetApi(Build.VERSION_CODES.M)
@CalledByNative
private static int getSignalStrengthLevel() {
return sInstance.mSignalLevel;
}
}