/*
* Copyright (C) 2007 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.internal.telephony;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
import android.os.RegistrantList;
import android.os.SystemProperties;
import android.telephony.ServiceState;
import android.util.Log;
import com.android.internal.R;
import com.android.internal.telephony.test.SimulatedRadioControl;
import java.util.List;
import java.util.Locale;
/**
* (<em>Not for SDK use</em>)
* A base implementation for the com.android.internal.telephony.Phone interface.
*
* Note that implementations of Phone.java are expected to be used
* from a single application thread. This should be the same thread that
* originally called PhoneFactory to obtain the interface.
*
* {@hide}
*
*/
public abstract class PhoneBase implements Phone {
private static final String LOG_TAG = "GSM";
protected final RegistrantList mPhoneStateRegistrants
= new RegistrantList();
protected final RegistrantList mNewRingingConnectionRegistrants
= new RegistrantList();
protected final RegistrantList mIncomingRingRegistrants
= new RegistrantList();
protected final RegistrantList mDisconnectRegistrants
= new RegistrantList();
protected final RegistrantList mServiceStateRegistrants
= new RegistrantList();
protected final RegistrantList mMmiCompleteRegistrants
= new RegistrantList();
protected final RegistrantList mMmiRegistrants
= new RegistrantList();
protected final RegistrantList mUnknownConnectionRegistrants
= new RegistrantList();
protected final RegistrantList mSuppServiceFailedRegistrants
= new RegistrantList();
protected Looper mLooper; /* to insure registrants are in correct thread*/
protected Context mContext;
/**
* PhoneNotifier is an abstraction for all system-wide
* state change notification. DefaultPhoneNotifier is
* used here unless running we're inside a unit test.
*/
protected PhoneNotifier mNotifier;
protected SimulatedRadioControl mSimulatedRadioControl;
boolean mUnitTestMode;
/**
* Constructs a PhoneBase in normal (non-unit test) mode.
*
* @param context Context object from hosting application
* @param notifier An instance of DefaultPhoneNotifier,
* unless unit testing.
*/
protected PhoneBase(PhoneNotifier notifier, Context context) {
this(notifier, context, false);
}
/**
* Constructs a PhoneBase in normal (non-unit test) mode.
*
* @param context Context object from hosting application
* @param notifier An instance of DefaultPhoneNotifier,
* unless unit testing.
* @param unitTestMode when true, prevents notifications
* of state change events
*/
protected PhoneBase(PhoneNotifier notifier, Context context,
boolean unitTestMode) {
this.mNotifier = notifier;
this.mContext = context;
mLooper = Looper.myLooper();
setLocaleByCarrier();
setUnitTestMode(unitTestMode);
}
// Inherited documentation suffices.
public Context getContext() {
return mContext;
}
// Inherited documentation suffices.
public void registerForPhoneStateChanged(Handler h, int what, Object obj) {
checkCorrectThread(h);
mPhoneStateRegistrants.addUnique(h, what, obj);
}
// Inherited documentation suffices.
public void unregisterForPhoneStateChanged(Handler h) {
mPhoneStateRegistrants.remove(h);
}
/**
* Notify registrants of a PhoneStateChanged.
* Subclasses of Phone probably want to replace this with a
* version scoped to their packages
*/
protected void notifyCallStateChangedP() {
AsyncResult ar = new AsyncResult(null, this, null);
mPhoneStateRegistrants.notifyRegistrants(ar);
}
// Inherited documentation suffices.
public void registerForUnknownConnection(Handler h, int what, Object obj) {
checkCorrectThread(h);
mUnknownConnectionRegistrants.addUnique(h, what, obj);
}
// Inherited documentation suffices.
public void unregisterForUnknownConnection(Handler h) {
mUnknownConnectionRegistrants.remove(h);
}
// Inherited documentation suffices.
public void registerForNewRingingConnection(
Handler h, int what, Object obj) {
checkCorrectThread(h);
mNewRingingConnectionRegistrants.addUnique(h, what, obj);
}
// Inherited documentation suffices.
public void unregisterForNewRingingConnection(Handler h) {
mNewRingingConnectionRegistrants.remove(h);
}
/**
* Notifiy registrants of a new ringing Connection.
* Subclasses of Phone probably want to replace this with a
* version scoped to their packages
*/
protected void notifyNewRingingConnectionP(Connection cn) {
AsyncResult ar = new AsyncResult(null, cn, null);
mNewRingingConnectionRegistrants.notifyRegistrants(ar);
}
// Inherited documentation suffices.
public void registerForIncomingRing(
Handler h, int what, Object obj) {
checkCorrectThread(h);
mIncomingRingRegistrants.addUnique(h, what, obj);
}
// Inherited documentation suffices.
public void unregisterForIncomingRing(Handler h) {
mIncomingRingRegistrants.remove(h);
}
// Inherited documentation suffices.
public void registerForDisconnect(Handler h, int what, Object obj) {
checkCorrectThread(h);
mDisconnectRegistrants.addUnique(h, what, obj);
}
// Inherited documentation suffices.
public void unregisterForDisconnect(Handler h) {
mDisconnectRegistrants.remove(h);
}
// Inherited documentation suffices.
public void registerForSuppServiceFailed(Handler h, int what, Object obj) {
checkCorrectThread(h);
mSuppServiceFailedRegistrants.addUnique(h, what, obj);
}
// Inherited documentation suffices.
public void unregisterForSuppServiceFailed(Handler h) {
mSuppServiceFailedRegistrants.remove(h);
}
// Inherited documentation suffices.
public void registerForMmiInitiate(Handler h, int what, Object obj) {
checkCorrectThread(h);
mMmiRegistrants.addUnique(h, what, obj);
}
// Inherited documentation suffices.
public void unregisterForMmiInitiate(Handler h) {
mMmiRegistrants.remove(h);
}
// Inherited documentation suffices.
public void registerForMmiComplete(Handler h, int what, Object obj) {
checkCorrectThread(h);
mMmiCompleteRegistrants.addUnique(h, what, obj);
}
// Inherited documentation suffices.
public void unregisterForMmiComplete(Handler h) {
checkCorrectThread(h);
mMmiCompleteRegistrants.remove(h);
}
/**
* Subclasses should override this. See documentation in superclass.
*/
public abstract List getPendingMmiCodes();
// Inherited documentation suffices.
public void setUnitTestMode(boolean f) {
mUnitTestMode = f;
}
// Inherited documentation suffices.
public boolean getUnitTestMode() {
return mUnitTestMode;
}
/**
* To be invoked when a voice call Connection disconnects.
*
* Subclasses of Phone probably want to replace this with a
* version scoped to their packages
*/
protected void notifyDisconnectP(Connection cn) {
AsyncResult ar = new AsyncResult(null, cn, null);
mDisconnectRegistrants.notifyRegistrants(ar);
}
// Inherited documentation suffices.
public void registerForServiceStateChanged(
Handler h, int what, Object obj) {
checkCorrectThread(h);
mServiceStateRegistrants.add(h, what, obj);
}
// Inherited documentation suffices.
public void unregisterForServiceStateChanged(Handler h) {
mServiceStateRegistrants.remove(h);
}
/**
* Subclasses of Phone probably want to replace this with a
* version scoped to their packages
*/
protected void notifyServiceStateChangedP(ServiceState ss) {
AsyncResult ar = new AsyncResult(null, ss, null);
mServiceStateRegistrants.notifyRegistrants(ar);
mNotifier.notifyServiceState(this);
}
// Inherited documentation suffices.
public SimulatedRadioControl getSimulatedRadioControl() {
return mSimulatedRadioControl;
}
/**
* Verifies the current thread is the same as the thread originally
* used in the initialization of this instance. Throws RuntimeException
* if not.
*
* @exception RuntimeException if the current thread is not
* the thread that originally obtained this PhoneBase instance.
*/
private void checkCorrectThread(Handler h) {
if (h.getLooper() != mLooper) {
throw new RuntimeException(
"com.android.internal.telephony.Phone must be used from within one thread");
}
}
/**
* Set the locale by matching the carrier string in
* a string-array resource
*/
private void setLocaleByCarrier() {
String carrier = SystemProperties.get("ro.carrier");
if (null == carrier || 0 == carrier.length()) {
return;
}
CharSequence[] carrierLocales = mContext.
getResources().getTextArray(R.array.carrier_locales);
for (int i = 0; i < carrierLocales.length-1; i+=2) {
String c = carrierLocales[i].toString();
String l = carrierLocales[i+1].toString();
if (carrier.equals(c)) {
String language = l.substring(0, 2);
String country = "";
if (l.length() >=5) {
country = l.substring(3, 5);
}
setSystemLocale(language, country);
return;
}
}
}
/**
* Utility code to set the system locale if it's not set already
* @param langauge Two character language code desired
* @param country Two character country code desired
*
* {@hide}
*/
public void setSystemLocale(String language, String country) {
String l = SystemProperties.get("persist.sys.language");
String c = SystemProperties.get("persist.sys.country");
if (null == language) {
return; // no match possible
}
language.toLowerCase();
if (null == country) {
country = "";
}
country = country.toUpperCase();
if((null == l || 0 == l.length()) && (null == c || 0 == c.length())) {
try {
// try to find a good match
String[] locales = mContext.getAssets().getLocales();
final int N = locales.length;
String bestMatch = null;
for(int i = 0; i < N; i++) {
if (locales[i]!=null && locales[i].length() >= 2 &&
locales[i].substring(0,2).equals(language)) {
if (locales[i].length() >= 5) {
if (locales[i].substring(3,5).equals(country)) {
bestMatch = locales[i];
break;
}
} else if (null == bestMatch) {
bestMatch = locales[i];
}
}
}
if (null != bestMatch) {
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
if (bestMatch.length() >= 5) {
config.locale = new Locale(bestMatch.substring(0,2),
bestMatch.substring(3,5));
} else {
config.locale = new Locale(bestMatch.substring(0,2));
}
config.userSetLocale = true;
am.updateConfiguration(config);
}
} catch (Exception e) {
// Intentionally left blank
}
}
}
}
|