blob: e8737688028207f5a7d03e8108daa760d5064eb2 [file] [log] [blame]
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.android.systemui.statusbar.policy;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.DeadObjectException;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import com.google.android.collect.Lists;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.Exception;
import java.util.ArrayList;
import java.lang.ref.WeakReference;
import org.codeaurora.internal.Client;
import org.codeaurora.internal.IExtTelephony;
import org.codeaurora.internal.INetworkCallback;
import org.codeaurora.internal.NetworkCallbackBase;
import org.codeaurora.internal.NrIconType;
import org.codeaurora.internal.ServiceUtil;
import org.codeaurora.internal.Status;
import org.codeaurora.internal.Token;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGroup;
public class FiveGServiceClient {
private static final String TAG = "FiveGServiceClient";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG)||true;
private static final int MESSAGE_REBIND = 1024;
private static final int MESSAGE_REINIT = MESSAGE_REBIND+1;
private static final int MESSAGE_NOTIFIY_MONITOR_CALLBACK = MESSAGE_REBIND+2;
private static final int MAX_RETRY = 4;
private static final int DELAY_MILLISECOND = 3000;
private static final int DELAY_INCREMENT = 2000;
private static FiveGServiceClient sInstance;
private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
mKeyguardUpdateMonitorCallbacks = Lists.newArrayList();
@VisibleForTesting
final SparseArray<IFiveGStateListener> mStatesListeners = new SparseArray<>();
private final SparseArray<FiveGServiceState> mCurrentServiceStates = new SparseArray<>();
private final SparseArray<FiveGServiceState> mLastServiceStates = new SparseArray<>();
private Context mContext;
private boolean mServiceConnected;
private IExtTelephony mNetworkService;
private String mPackageName;
private Client mClient;
private int mBindRetryTimes = 0;
private int mInitRetryTimes = 0;
public static class FiveGServiceState{
private int mNrIconType;
private MobileIconGroup mIconGroup;
public FiveGServiceState(){
mNrIconType = NrIconType.INVALID;
mIconGroup = TelephonyIcons.UNKNOWN;
}
public boolean isNrIconTypeValid() {
return mNrIconType != NrIconType.INVALID && mNrIconType != NrIconType.TYPE_NONE;
}
@VisibleForTesting
public MobileIconGroup getIconGroup() {
return mIconGroup;
}
@VisibleForTesting
int getNrIconType() {
return mNrIconType;
}
public void copyFrom(FiveGServiceState state) {
this.mIconGroup = state.mIconGroup;
this.mNrIconType = state.mNrIconType;
}
public boolean equals(FiveGServiceState state) {
return this.mIconGroup == state.mIconGroup
&& this.mNrIconType == state.mNrIconType;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("mNrIconType=").append(mNrIconType).append(", ").
append("mIconGroup=").append(mIconGroup);
return builder.toString();
}
}
public FiveGServiceClient(Context context) {
mContext = context;
mPackageName = mContext.getPackageName();
}
public static FiveGServiceClient getInstance(Context context) {
if ( sInstance == null ) {
sInstance = new FiveGServiceClient(context);
}
return sInstance;
}
public void registerCallback(KeyguardUpdateMonitorCallback callback) {
mKeyguardUpdateMonitorCallbacks.add(
new WeakReference<KeyguardUpdateMonitorCallback>(callback));
}
public void registerListener(int phoneId, IFiveGStateListener listener) {
Log.d(TAG, "registerListener phoneId=" + phoneId);
resetState(phoneId);
mStatesListeners.put(phoneId, listener);
if ( !isServiceConnected() ) {
binderService();
}else{
initFiveGServiceState(phoneId);
}
}
private void resetState(int phoneId) {
Log.d(TAG, "resetState phoneId=" + phoneId);
FiveGServiceState currentState = getCurrentServiceState(phoneId);
currentState.mNrIconType = NrIconType.INVALID;
currentState.mIconGroup = TelephonyIcons.UNKNOWN;
FiveGServiceState lastState = getLastServiceState(phoneId);
lastState.mNrIconType = NrIconType.INVALID;
lastState.mIconGroup = TelephonyIcons.UNKNOWN;
}
public void unregisterListener(int phoneId) {
Log.d(TAG, "unregisterListener phoneId=" + phoneId);
mStatesListeners.remove(phoneId);
mCurrentServiceStates.remove(phoneId);
mLastServiceStates.remove(phoneId);
}
private void binderService() {
boolean success = ServiceUtil.bindService(mContext, mServiceConnection);
Log.d(TAG, " bind service " + success);
if ( !success && mBindRetryTimes < MAX_RETRY && !mHandler.hasMessages(MESSAGE_REBIND)) {
mHandler.sendEmptyMessageDelayed(MESSAGE_REBIND,
DELAY_MILLISECOND + mBindRetryTimes*DELAY_INCREMENT);
mBindRetryTimes+=1;
}
}
public boolean isServiceConnected() {
return mServiceConnected;
}
@VisibleForTesting
public FiveGServiceState getCurrentServiceState(int phoneId) {
return getServiceState(phoneId, mCurrentServiceStates);
}
private FiveGServiceState getLastServiceState(int phoneId) {
return getServiceState(phoneId, mLastServiceStates);
}
private static FiveGServiceState getServiceState(int key,
SparseArray<FiveGServiceState> array) {
FiveGServiceState state = array.get(key);
if ( state == null ) {
state = new FiveGServiceState();
array.put(key, state);
}
return state;
}
private void notifyListenersIfNecessary(int phoneId) {
FiveGServiceState currentState = getCurrentServiceState(phoneId);
FiveGServiceState lastState = getLastServiceState(phoneId);
if ( !currentState.equals(lastState) ) {
if ( DEBUG ) {
Log.d(TAG, "phoneId(" + phoneId + ") Change in state from " + lastState + " \n"+
"\tto " + currentState);
}
lastState.copyFrom(currentState);
IFiveGStateListener listener = mStatesListeners.get(phoneId);
if (listener != null) {
listener.onStateChanged(currentState);
}
mHandler.sendEmptyMessage(MESSAGE_NOTIFIY_MONITOR_CALLBACK);
}
}
private void initFiveGServiceState() {
Log.d(TAG, "initFiveGServiceState size=" + mStatesListeners.size());
for( int i=0; i < mStatesListeners.size(); ++i ) {
int phoneId = mStatesListeners.keyAt(i);
initFiveGServiceState(phoneId);
}
}
private void initFiveGServiceState(int phoneId) {
Log.d(TAG, "mNetworkService=" + mNetworkService + " mClient=" + mClient);
if ( mNetworkService != null && mClient != null) {
Log.d(TAG, "query 5G service state for phoneId " + phoneId);
try {
Token token = mNetworkService.queryNrIconType(phoneId, mClient);
Log.d(TAG, "queryNrIconType result:" + token);
}catch(DeadObjectException e) {
Log.e(TAG, "initFiveGServiceState: Exception = " + e);
Log.d(TAG, "try to re-binder service");
mInitRetryTimes = 0;
mServiceConnected = false;
mNetworkService = null;
mClient = null;
binderService();
}catch (Exception e) {
Log.d(TAG, "initFiveGServiceState: Exception = " + e);
if ( mInitRetryTimes < MAX_RETRY && !mHandler.hasMessages(MESSAGE_REINIT) ) {
mHandler.sendEmptyMessageDelayed(MESSAGE_REINIT,
DELAY_MILLISECOND + mInitRetryTimes*DELAY_INCREMENT);
mInitRetryTimes +=1;
}
}
}
}
@VisibleForTesting
void update5GIcon(FiveGServiceState state,int phoneId) {
state.mIconGroup = getNrIconGroup(state.mNrIconType, phoneId);
}
private MobileIconGroup getNrIconGroup(int nrIconType , int phoneId) {
MobileIconGroup iconGroup = TelephonyIcons.UNKNOWN;
switch (nrIconType){
case NrIconType.TYPE_5G_BASIC:
iconGroup = TelephonyIcons.FIVE_G_BASIC;
break;
case NrIconType.TYPE_5G_UWB:
iconGroup = TelephonyIcons.FIVE_G_UWB;
break;
}
return iconGroup;
}
private void notifyMonitorCallback() {
for (int i = 0; i < mKeyguardUpdateMonitorCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mKeyguardUpdateMonitorCallbacks.get(i).get();
if (cb != null) {
cb.onRefreshCarrierInfo();
}
}
}
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
int what = msg.what;
switch ( msg.what ) {
case MESSAGE_REBIND:
binderService();
break;
case MESSAGE_REINIT:
initFiveGServiceState();
break;
case MESSAGE_NOTIFIY_MONITOR_CALLBACK:
notifyMonitorCallback();
break;
}
}
};
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected:" + service);
try {
mNetworkService = IExtTelephony.Stub.asInterface(service);
mClient = mNetworkService.registerCallback(mPackageName, mCallback);
mServiceConnected = true;
initFiveGServiceState();
Log.d(TAG, "Client = " + mClient);
} catch (Exception e) {
Log.d(TAG, "onServiceConnected: Exception = " + e);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected:" + name);
cleanup();
}
@Override
public void onBindingDied(ComponentName name) {
Log.d(TAG, "onBindingDied:" + name);
cleanup();
if ( mBindRetryTimes < MAX_RETRY ) {
Log.d(TAG, "try to re-bind");
mHandler.sendEmptyMessageDelayed(MESSAGE_REBIND,
DELAY_MILLISECOND+mBindRetryTimes*DELAY_INCREMENT);
}
}
private void cleanup() {
Log.d(TAG, "cleanup");
mServiceConnected = false;
mNetworkService = null;
mClient = null;
}
};
@VisibleForTesting
protected INetworkCallback mCallback = new NetworkCallbackBase() {
@Override
public void onNrIconType(int slotId, Token token, Status status, NrIconType
nrIconType) throws RemoteException {
Log.d(TAG,
"onNrIconType: slotId = " + slotId + " token = " + token + " " + "status"
+ status + " NrIconType = " + nrIconType);
if (status.get() == Status.SUCCESS) {
FiveGServiceState state = getCurrentServiceState(slotId);
state.mNrIconType = nrIconType.get();
update5GIcon(state, slotId);
notifyListenersIfNecessary(slotId);
}
}
};
public interface IFiveGStateListener {
public void onStateChanged(FiveGServiceState state);
}
}