blob: 7035881e07d4f93114bfd875dca8b8fdb7d02b83 [file] [log] [blame]
/*
* Copyright (C) 2018 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.phone.ecc;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.AsyncTask;
import android.provider.Settings;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
import android.telephony.CellIdentityWcdma;
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
import android.telephony.CellInfoWcdma;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import com.android.internal.telephony.MccTable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Helper for retrieve ECC info for current country.
*/
public class EccInfoHelper {
private static final String LOG_TAG = "EccInfoHelper";
// country ISO to ECC list data source
private IsoToEccRepository mEccRepo;
/**
* Callback for {@link #getCountryEccInfoAsync}.
*/
public interface CountryEccInfoResultCallback {
/**
* Called if successfully get country ECC info.
*
* @param iso Detected current country ISO.
* @param countryEccInfo The EccInfo of current country.
*/
void onSuccess(@NonNull String iso, @NonNull CountryEccInfo countryEccInfo);
/**
* Called if failed to get country ISO.
*/
void onDetectCountryFailed();
/**
* Called if failed to get ECC info for given country ISO.
*
* @param iso Detected current country ISO.
*/
void onRetrieveCountryEccInfoFailed(@NonNull String iso);
}
/**
* Constructor of EccInfoHelper
*
* @param eccRepository A repository for ECC info, indexed by country ISO.
*/
public EccInfoHelper(@NonNull IsoToEccRepository eccRepository) {
mEccRepo = eccRepository;
}
/**
* Get ECC info for current location, base on detected country ISO.
* It's possible we cannot detect current country, ex. device is in airplane mode,
* or there's no available base station near by.
*
* @param context The context used to access resources.
* @param callback Callback for result.
*/
public void getCountryEccInfoAsync(final @NonNull Context context,
final CountryEccInfoResultCallback callback) {
new AsyncTask<Void, Void, Pair<String, CountryEccInfo>>() {
@Override
protected Pair<String, CountryEccInfo> doInBackground(Void... voids) {
String iso = getCurrentCountryIso(context);
if (TextUtils.isEmpty(iso)) {
return null;
}
CountryEccInfo dialableCountryEccInfo;
try {
// access data source in background thread to avoid possible file IO caused ANR.
CountryEccInfo rawEccInfo = mEccRepo.getCountryEccInfo(context, iso);
dialableCountryEccInfo = getDialableCountryEccInfo(rawEccInfo);
} catch (IOException e) {
Log.e(LOG_TAG, "Failed to retrieve ECC: " + e.getMessage());
dialableCountryEccInfo = null;
}
return new Pair<>(iso, dialableCountryEccInfo);
}
@Override
protected void onPostExecute(Pair<String, CountryEccInfo> result) {
if (callback != null) {
if (result == null) {
callback.onDetectCountryFailed();
} else {
String iso = result.first;
CountryEccInfo countryEccInfo = result.second;
if (countryEccInfo == null) {
callback.onRetrieveCountryEccInfoFailed(iso);
} else {
callback.onSuccess(iso, countryEccInfo);
}
}
}
}
}.execute();
}
private @NonNull CountryEccInfo getDialableCountryEccInfo(CountryEccInfo countryEccInfo) {
ArrayList<EccInfo> dialableECCList = new ArrayList<>();
String dialableFallback = null;
// filter out non-dialable ECC
if (countryEccInfo != null) {
for (EccInfo entry : countryEccInfo.getEccInfoList()) {
if (PhoneNumberUtils.isEmergencyNumber(entry.getNumber())) {
dialableECCList.add(entry);
}
}
String defaultFallback = countryEccInfo.getFallbackEcc();
if (PhoneNumberUtils.isEmergencyNumber(defaultFallback)) {
dialableFallback = defaultFallback;
}
}
return new CountryEccInfo(dialableFallback, dialableECCList);
}
private @Nullable String getCurrentCountryIso(@NonNull Context context) {
// Do not detect country ISO if airplane mode is on
int airplaneMode = Settings.System.getInt(context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0);
if (airplaneMode != 0) {
Log.d(LOG_TAG, "Airplane mode is on, do not get country ISO.");
return null;
}
TelephonyManager tm = (TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE);
String iso = tm.getNetworkCountryIso();
Log.d(LOG_TAG, "Current country ISO is " + iso);
if (TextUtils.isEmpty(iso)) {
// XXX: according to ServiceStateTracker's implementation, retrieve cell info in a
// thread other than TelephonyManager's main thread.
String mcc = getCurrentMccFromCellInfo(context);
iso = countryCodeForMcc(mcc);
Log.d(LOG_TAG, "Current mcc is " + mcc + ", mapping to ISO: " + iso);
}
return iso;
}
private String countryCodeForMcc(String mcc) {
try {
return MccTable.countryCodeForMcc(Integer.parseInt(mcc));
} catch (NumberFormatException ex) {
return "";
}
}
// XXX: According to ServiceStateTracker implementation, to actually get current cell info,
// this method must be called in a separate thread from ServiceStateTracker, which is the
// main thread of Telephony service.
private @Nullable String getCurrentMccFromCellInfo(@NonNull Context context) {
// retrieve mcc info from base station even no SIM present.
TelephonyManager tm = (TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE);
List<CellInfo> cellInfos = tm.getAllCellInfo();
String mcc = null;
if (cellInfos != null) {
for (CellInfo ci : cellInfos) {
if (ci instanceof CellInfoGsm) {
CellInfoGsm cellInfoGsm = (CellInfoGsm) ci;
CellIdentityGsm cellIdentityGsm = cellInfoGsm.getCellIdentity();
mcc = cellIdentityGsm.getMccString();
break;
} else if (ci instanceof CellInfoWcdma) {
CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ci;
CellIdentityWcdma cellIdentityWcdma = cellInfoWcdma.getCellIdentity();
mcc = cellIdentityWcdma.getMccString();
break;
} else if (ci instanceof CellInfoLte) {
CellInfoLte cellInfoLte = (CellInfoLte) ci;
CellIdentityLte cellIdentityLte = cellInfoLte.getCellIdentity();
mcc = cellIdentityLte.getMccString();
break;
}
}
Log.d(LOG_TAG, "Retrieve MCC from cell info list: " + mcc);
} else {
Log.w(LOG_TAG, "Cannot get cell info list.");
}
return mcc;
}
}