Merge "Add service CellularNetworkService in manifest."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index dad431c..32c8ade 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -181,6 +181,7 @@
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<application android:name="PhoneApp"
android:persistent="true"
@@ -385,6 +386,15 @@
</intent-filter>
</activity>
+ <activity android:name="GsmUmtsCallBarringOptions"
+ android:label="@string/labelCallBarring"
+ android:configChanges="orientation|screenSize|keyboardHidden"
+ android:theme="@style/CallSettingsWithoutDividerTheme">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
<activity android:name="GsmUmtsAdditionalCallOptions"
android:label="@string/labelGSMMore"
android:configChanges="orientation|screenSize|keyboardHidden"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 61c5f97..fca8acf 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -50,6 +50,21 @@
</attr>
</declare-styleable>
+ <declare-styleable name="CallBarringEditPreference">
+ <!-- AO: All outgoing, CommandsInterface.CB_FACILITY_BAOC. -->
+ <!-- OI: Outgoing international, CommandsInterface.CB_FACILITY_BAOIC. -->
+ <!-- OX: Outgoing international roaming, CommandsInterface.CB_FACILITY_BAOICxH. -->
+ <!-- AI: All incoming, CommandsInterface.CB_FACILITY_BAIC. -->
+ <!-- IR: Incoming international roaming, CommandsInterface.CB_FACILITY_BAICr. -->
+ <!-- BA: Disable all, CommandsInterface.CB_FACILITY_BA_ALL -->
+ <attr name="facility" format="string" />
+
+ <!-- Message when password is not in use, and call barring is enabled -->
+ <attr name="dialogMessageEnabledNoPwd" format="string" />
+ <!-- Message when password is not in use, and call barring is disabled -->
+ <attr name="dialogMessageDisabledNoPwd" format="string" />
+ </declare-styleable>
+
<attr name="preferenceBackgroundColor" format="color" />
<attr name="emergencyButtonBackgroundColor" format="color" />
<attr name="dialpadTheme" format="reference" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 1b612c9..855fa92 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -241,4 +241,10 @@
<!-- Whether the cellular radio is allowed to be power down when the Bluetooth can provide the data/call capabilities -->
<bool name="config_allowRadioPowerDownOnBluetooth">false</bool>
+
+ <!-- Whether the device supports the AudioManager Telephony audio device and output onto this
+ device using {@link AudioDeviceInfo#TYPE_TELEPHONY}.
+ When this is true, the Telephony stack is able to add additional audio to the outgoing
+ audio stream which the remote party will be able to hear. -->
+ <bool name="config_support_telephony_audio_device">false</bool>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3737fd1..7d662f5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1603,4 +1603,71 @@
<string name="clh_callFailed_protocol_Error_unspecified_txt">Protocol error, unspecified</string>
<!-- In-call screen: call failure reason (Cause Number 127) -->
<string name="clh_callFailed_interworking_unspecified_txt">Interworking, unspecified</string>
+
+ <!-- Call settings screen, setting option name -->
+ <string name="labelCallBarring">Call barring</string>
+ <!-- Call barring settings screen, setting summary text when a call barring option is activated -->
+ <string name="sum_call_barring_enabled">On</string>
+ <!-- Call barring settings screen, setting summary text when a call barring option is deactivated -->
+ <string name="sum_call_barring_disabled">Off</string>
+ <!-- Call barring settings screen, setting option name -->
+ <string name="call_barring_baoc">All outgoing</string>
+ <!-- Call barring settings screen, Disable blocking of all outgoing calls -->
+ <string name="call_barring_baoc_enabled">Disable blocking of all outgoing calls?</string>
+ <!-- Call barring settings screen, Block all outgoing calls -->
+ <string name="call_barring_baoc_disabled">Block all outgoing calls?</string>
+ <!-- Call barring settings screen, setting option name -->
+ <string name="call_barring_baoic">Outgoing international</string>
+ <!-- Call barring settings screen, Disable blocking of outgoing international calls -->
+ <string name="call_barring_baoic_enabled">Disable blocking of outgoing international calls?</string>
+ <!-- Call barring settings screen, Block outgoing international calls-->
+ <string name="call_barring_baoic_disabled">Block outgoing international calls?</string>
+ <!-- Call barring settings screen, setting option name -->
+ <string name="call_barring_baoicr">Outgoing international roaming</string>
+ <!-- Call barring settings screen, Disable blocking of outgoing international roaming -->
+ <string name="call_barring_baoicr_enabled">Disable blocking of outgoing international roaming?</string>
+ <!-- Call barring settings screen, Block outgoing international roaming -->
+ <string name="call_barring_baoicr_disabled">Block outgoing international roaming?</string>
+ <!-- Call barring settings screen, setting option name -->
+ <string name="call_barring_baic">All incoming</string>
+ <!-- Call barring settings screen, Disable blocking of all incoming calls -->
+ <string name="call_barring_baic_enabled">Disable blocking of all incoming calls?</string>
+ <!-- Call barring settings screen, Block all incoming calls -->
+ <string name="call_barring_baic_disabled">Block all incoming calls?</string>
+ <!-- Call barring settings screen, setting option name -->
+ <string name="call_barring_baicr">Incoming international roaming</string>
+ <!-- Call barring settings screen, Disable blocking of all incoming international roaming -->
+ <string name="call_barring_baicr_enabled">Disable blocking of all incoming international roaming?</string>
+ <!-- Call barring settings screen, Block incoming international roaming-->
+ <string name="call_barring_baicr_disabled">Block incoming international roaming?</string>
+ <!-- Call barring settings screen, setting option name -->
+ <string name="call_barring_deactivate_all">Deactivate all</string>
+ <!-- Call barring settings screen, setting summary text when BAAll check box is selected -->
+ <string name="call_barring_deactivate_all_description">Deactivate all call barring settings</string>
+ <!-- Call barring settings screen, deactivate all successfully -->
+ <string name="call_barring_deactivate_success">Call barring deactivated</string>
+ <!-- Call barring settings screen, change password -->
+ <string name="call_barring_change_pwd">Change password</string>
+ <!-- Call barring settings screen, change password -->
+ <string name="call_barring_change_pwd_description">Change call barring password</string>
+ <!-- Call barring settings screen, not possible to change call barring password -->
+ <string name="call_barring_change_pwd_description_disabled">Cannot change call barring password.</string>
+ <!-- Call barring settings screen, change password -->
+ <string name="call_barring_pwd_not_match">Passwords do not match</string>
+ <!-- Call barring settings screen, change password -->
+ <string name="call_barring_right_pwd_number">Enter a password with 4 numbers</string>
+ <!-- Call barring settings screen, change password -->
+ <string name="call_barring_change_pwd_success">Password changed</string>
+ <!-- Call barring settings screen, change password -->
+ <string name="call_barring_old_pwd">Old password</string>
+ <!-- Call barring settings screen, change password -->
+ <string name="call_barring_new_pwd">New password</string>
+ <!-- Call barring settings screen, change password -->
+ <string name="call_barring_confirm_pwd">Confirm password</string>
+ <!-- Call forwarding dialog box, text field label -->
+ <string name="messageCallBarring">Enter password</string>
+ <!-- Call barring settings screen, section heading -->
+ <string name="call_barring_settings">Call barring settings</string>
+ <!-- Call barring settings screen, deactivate all call barring settings -->
+ <string name="call_barring_deactivate_all_no_password">Deactivate all call barring settings?</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index f8cd9ec..2b893e5 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -304,4 +304,7 @@
<item name="android:backgroundDimEnabled">false</item>
</style>
+ <style name="CallSettingsWithoutDividerTheme" parent="SettingsLight">
+ <item name="android:listDivider">@null</item>
+ </style>
</resources>
diff --git a/res/xml/callbarring_options.xml b/res/xml/callbarring_options.xml
new file mode 100644
index 0000000..6f2c48a
--- /dev/null
+++ b/res/xml/callbarring_options.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:phone="http://schemas.android.com/apk/res/com.android.phone"
+ android:title="@string/call_barring_settings">
+
+ <!-- Note for all com.android.phone.EditPinPreference objects
+
+ The last several attributes are for use with the EditText field
+ in the dialog. These attributes are forwarded to that field
+ when the edittext is created. The attributes include:
+ 1. android:singleLine
+ 2. android:autoText -->
+
+ <!-- All outgoing -->
+ <com.android.phone.CallBarringEditPreference
+ android:key="button_baoc_key"
+ android:title="@string/call_barring_baoc"
+ android:persistent="false"
+ android:summaryOn="@string/sum_call_barring_enabled"
+ android:summaryOff="@string/sum_call_barring_disabled"
+ android:dialogTitle="@string/call_barring_baoc"
+ phone:dialogMessageEnabledNoPwd="@string/call_barring_baoc_enabled"
+ phone:dialogMessageDisabledNoPwd="@string/call_barring_baoc_disabled"
+ phone:facility="AO"
+ android:singleLine="true"
+ android:autoText="false"/>
+
+ <!-- Outgoing international -->
+ <com.android.phone.CallBarringEditPreference
+ android:key="button_baoic_key"
+ android:title="@string/call_barring_baoic"
+ android:persistent="false"
+ android:summaryOn="@string/sum_call_barring_enabled"
+ android:summaryOff="@string/sum_call_barring_disabled"
+ android:dialogTitle="@string/call_barring_baoic"
+ phone:dialogMessageEnabledNoPwd="@string/call_barring_baoic_enabled"
+ phone:dialogMessageDisabledNoPwd="@string/call_barring_baoic_disabled"
+ phone:facility="OI"
+ android:dependency="button_baoc_key"
+ android:singleLine="true"
+ android:autoText="false"/>
+
+ <!-- Outgoing international roaming -->
+ <com.android.phone.CallBarringEditPreference
+ android:key="button_baoicxh_key"
+ android:title="@string/call_barring_baoicr"
+ android:persistent="false"
+ android:summaryOn="@string/sum_call_barring_enabled"
+ android:summaryOff="@string/sum_call_barring_disabled"
+ android:dialogTitle="@string/call_barring_baoicr"
+ phone:dialogMessageEnabledNoPwd="@string/call_barring_baoicr_enabled"
+ phone:dialogMessageDisabledNoPwd="@string/call_barring_baoicr_disabled"
+ phone:facility="OX"
+ android:dependency="button_baoc_key"
+ android:singleLine="true"
+ android:autoText="false"/>
+
+ <!-- All incoming -->
+ <com.android.phone.CallBarringEditPreference
+ android:key="button_baic_key"
+ android:title="@string/call_barring_baic"
+ android:persistent="false"
+ android:summaryOn="@string/sum_call_barring_enabled"
+ android:summaryOff="@string/sum_call_barring_disabled"
+ android:dialogTitle="@string/call_barring_baic"
+ phone:dialogMessageEnabledNoPwd="@string/call_barring_baic_enabled"
+ phone:dialogMessageDisabledNoPwd="@string/call_barring_baic_disabled"
+ phone:facility="AI"
+ android:singleLine="true"
+ android:autoText="false"/>
+
+ <!-- Incoming international roaming -->
+ <com.android.phone.CallBarringEditPreference
+ android:key="button_baicr_key"
+ android:title="@string/call_barring_baicr"
+ android:persistent="false"
+ android:summaryOn="@string/sum_call_barring_enabled"
+ android:summaryOff="@string/sum_call_barring_disabled"
+ android:dialogTitle="@string/call_barring_baicr"
+ phone:dialogMessageEnabledNoPwd="@string/call_barring_baicr_enabled"
+ phone:dialogMessageDisabledNoPwd="@string/call_barring_baicr_disabled"
+ phone:facility="IR"
+ android:dependency="button_baic_key"
+ android:singleLine="true"
+ android:autoText="false"/>
+
+ <!-- Disable all -->
+ <com.android.phone.CallBarringDeselectAllPreference
+ android:key="button_ba_all_key"
+ android:title="@string/call_barring_deactivate_all"
+ android:persistent="false"
+ android:dialogTitle="@string/call_barring_deactivate_all"
+ android:summary="@string/call_barring_deactivate_all_description"/>
+
+ <!-- Change password -->
+ <com.android.phone.settings.fdn.EditPinPreference
+ android:key="button_change_pw_key"
+ android:title="@string/call_barring_change_pwd"
+ android:dialogTitle="@string/call_barring_change_pwd"
+ android:summary="@string/call_barring_change_pwd_description"
+ android:persistent="false"/>
+</PreferenceScreen>
diff --git a/res/xml/gsm_umts_call_options.xml b/res/xml/gsm_umts_call_options.xml
index 5f3dfe4..774aec8 100644
--- a/res/xml/gsm_umts_call_options.xml
+++ b/res/xml/gsm_umts_call_options.xml
@@ -10,6 +10,11 @@
android:persistent="false" />
<PreferenceScreen
+ android:key="call_barring_key"
+ android:title="@string/labelCallBarring"
+ android:persistent="false" />
+
+ <PreferenceScreen
android:key="additional_gsm_call_settings_key"
android:title="@string/additional_gsm_call_settings"
android:persistent="false" />
diff --git a/src/com/android/phone/CallBarringDeselectAllPreference.java b/src/com/android/phone/CallBarringDeselectAllPreference.java
new file mode 100644
index 0000000..153bc0c
--- /dev/null
+++ b/src/com/android/phone/CallBarringDeselectAllPreference.java
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.telephony.ServiceState;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.phone.settings.fdn.EditPinPreference;
+
+/**
+ * This preference represents the status of disable all barring option.
+ */
+public class CallBarringDeselectAllPreference extends EditPinPreference {
+ private static final String LOG_TAG = "CallBarringDeselectAllPreference";
+ private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+ private boolean mShowPassword;
+ private Phone mPhone;
+
+ /**
+ * CallBarringDeselectAllPreference constructor.
+ *
+ * @param context The context of view.
+ * @param attrs The attributes of the XML tag that is inflating EditTextPreference.
+ */
+ public CallBarringDeselectAllPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void showDialog(Bundle state) {
+ // Finds out if the password field should be shown or not.
+ ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
+ mShowPassword = !(imsPhone != null
+ && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+ || imsPhone.isUtEnabled()));
+
+ // Selects dialog message depending on if the password field is shown or not.
+ setDialogMessage(getContext().getString(mShowPassword
+ ? R.string.messageCallBarring : R.string.call_barring_deactivate_all_no_password));
+
+ if (DBG) {
+ Log.d(LOG_TAG, "showDialog: mShowPassword: " + mShowPassword);
+ }
+
+ super.showDialog(state);
+ }
+
+ void init(Phone phone) {
+ if (DBG) {
+ Log.d(LOG_TAG, "init: phoneId = " + phone.getPhoneId());
+ }
+ mPhone = phone;
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ final EditText editText = (EditText) view.findViewById(android.R.id.edit);
+ if (editText != null) {
+ // Hide the input-text-line if the password is not shown.
+ editText.setVisibility(mShowPassword ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ @Override
+ protected boolean needInputMethod() {
+ // Input method should only be displayed if the password-field is shown.
+ return mShowPassword;
+ }
+
+ /**
+ * Returns whether the password field is shown.
+ */
+ boolean isPasswordShown() {
+ return mShowPassword;
+ }
+}
diff --git a/src/com/android/phone/CallBarringEditPreference.java b/src/com/android/phone/CallBarringEditPreference.java
new file mode 100644
index 0000000..72b3ea5
--- /dev/null
+++ b/src/com/android/phone/CallBarringEditPreference.java
@@ -0,0 +1,383 @@
+/*
+ * 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;
+
+import static com.android.phone.TimeConsumingPreferenceActivity.RESPONSE_ERROR;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.ServiceState;
+import android.text.method.DigitsKeyListener;
+import android.text.method.PasswordTransformationMethod;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.phone.settings.fdn.EditPinPreference;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This preference represents the status of call barring options, enabling/disabling
+ * the call barring option will prompt the user for the current password.
+ */
+public class CallBarringEditPreference extends EditPinPreference {
+ private static final String LOG_TAG = "CallBarringEditPreference";
+ private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+ private String mFacility;
+ boolean mIsActivated = false;
+ private CharSequence mEnableText;
+ private CharSequence mDisableText;
+ private CharSequence mSummaryOn;
+ private CharSequence mSummaryOff;
+ private CharSequence mDialogMessageEnabled;
+ private CharSequence mDialogMessageDisabled;
+ private int mButtonClicked;
+ private boolean mShowPassword;
+ private final MyHandler mHandler = new MyHandler(this);
+ private Phone mPhone;
+ private TimeConsumingPreferenceListener mTcpListener;
+
+ private static final int PW_LENGTH = 4;
+
+ /**
+ * CallBarringEditPreference constructor.
+ *
+ * @param context The context of view.
+ * @param attrs The attributes of the XML tag that is inflating EditTextPreference.
+ */
+ public CallBarringEditPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ // Get the summary settings, use CheckBoxPreference as the standard.
+ TypedArray typedArray = context.obtainStyledAttributes(attrs,
+ android.R.styleable.CheckBoxPreference, 0, 0);
+ mSummaryOn = typedArray.getString(android.R.styleable.CheckBoxPreference_summaryOn);
+ mSummaryOff = typedArray.getString(android.R.styleable.CheckBoxPreference_summaryOff);
+ mDisableText = context.getText(R.string.disable);
+ mEnableText = context.getText(R.string.enable);
+ typedArray.recycle();
+
+ // Get default phone
+ mPhone = PhoneFactory.getDefaultPhone();
+
+ typedArray = context.obtainStyledAttributes(attrs,
+ R.styleable.CallBarringEditPreference, 0, R.style.EditPhoneNumberPreference);
+ mFacility = typedArray.getString(R.styleable.CallBarringEditPreference_facility);
+ mDialogMessageEnabled = typedArray.getString(
+ R.styleable.CallBarringEditPreference_dialogMessageEnabledNoPwd);
+ mDialogMessageDisabled = typedArray.getString(
+ R.styleable.CallBarringEditPreference_dialogMessageDisabledNoPwd);
+ typedArray.recycle();
+ }
+
+ /**
+ * CallBarringEditPreference constructor.
+ *
+ * @param context The context of view.
+ */
+ public CallBarringEditPreference(Context context) {
+ this(context, null);
+ }
+
+ void init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
+ if (DBG) {
+ Log.d(LOG_TAG, "init: phone id = " + phone.getPhoneId());
+ }
+ mPhone = phone;
+
+ mTcpListener = listener;
+ if (!skipReading) {
+ // Query call barring status
+ mPhone.getCallBarring(mFacility, "", mHandler.obtainMessage(
+ MyHandler.MESSAGE_GET_CALL_BARRING), 0);
+ if (mTcpListener != null) {
+ mTcpListener.onStarted(this, true);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ super.onClick(dialog, which);
+ mButtonClicked = which;
+ }
+
+ @Override
+ protected boolean needInputMethod() {
+ // Input method should only be displayed if the password-field is shown.
+ return mShowPassword;
+ }
+
+ void setInputMethodNeeded(boolean needed) {
+ mShowPassword = needed;
+ }
+
+ @Override
+ protected void showDialog(Bundle state) {
+ setShowPassword();
+ if (mShowPassword) {
+ setDialogMessage(getContext().getString(R.string.messageCallBarring));
+ } else {
+ setDialogMessage(mIsActivated ? mDialogMessageEnabled : mDialogMessageDisabled);
+ }
+
+ if (DBG) {
+ Log.d(LOG_TAG, "showDialog: mShowPassword: " + mShowPassword
+ + ", mIsActivated: " + mIsActivated);
+ }
+
+ super.showDialog(state);
+ }
+
+ @Override
+ protected void onBindView(View view) {
+ super.onBindView(view);
+
+ // Sync the summary view
+ TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
+ if (summaryView != null) {
+ CharSequence sum;
+ int vis;
+
+ // Set summary depending upon mode
+ if (mIsActivated) {
+ sum = (mSummaryOn == null) ? getSummary() : mSummaryOn;
+ } else {
+ sum = (mSummaryOff == null) ? getSummary() : mSummaryOff;
+ }
+
+ if (sum != null) {
+ summaryView.setText(sum);
+ vis = View.VISIBLE;
+ } else {
+ vis = View.GONE;
+ }
+
+ if (vis != summaryView.getVisibility()) {
+ summaryView.setVisibility(vis);
+ }
+ }
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+ builder.setPositiveButton(null, null);
+ builder.setNeutralButton(mIsActivated ? mDisableText : mEnableText, this);
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+ // Default the button clicked to be the cancel button.
+ mButtonClicked = DialogInterface.BUTTON_NEGATIVE;
+
+ final EditText editText = (EditText) view.findViewById(android.R.id.edit);
+ if (editText != null) {
+ editText.setSingleLine(true);
+ editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+ editText.setKeyListener(DigitsKeyListener.getInstance());
+
+ // Hide the input-text-line if the password is not shown.
+ editText.setVisibility(mShowPassword ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+ if (DBG) {
+ Log.d(LOG_TAG, "onDialogClosed: mButtonClicked=" + mButtonClicked + ", positiveResult="
+ + positiveResult);
+ }
+ if (mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
+ String password = null;
+ if (mShowPassword) {
+ password = getEditText().getText().toString();
+
+ // Check if the password is valid.
+ if (password == null || password.length() != PW_LENGTH) {
+ Toast.makeText(getContext(),
+ getContext().getString(R.string.call_barring_right_pwd_number),
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+
+ if (DBG) {
+ Log.d(LOG_TAG, "onDialogClosed: password=" + password);
+ }
+ // Send set call barring message to RIL layer.
+ mPhone.setCallBarring(mFacility, !mIsActivated, password,
+ mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_BARRING), 0);
+ if (mTcpListener != null) {
+ mTcpListener.onStarted(this, false);
+ }
+ }
+ }
+
+ void handleCallBarringResult(boolean status) {
+ mIsActivated = status;
+ if (DBG) {
+ Log.d(LOG_TAG, "handleCallBarringResult: mIsActivated=" + mIsActivated);
+ }
+ }
+
+ void updateSummaryText() {
+ notifyChanged();
+ notifyDependencyChange(shouldDisableDependents());
+ }
+
+ private void setShowPassword() {
+ ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
+ mShowPassword = !(imsPhone != null
+ && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+ || imsPhone.isUtEnabled()));
+ }
+
+ @Override
+ public boolean shouldDisableDependents() {
+ return mIsActivated;
+ }
+
+ // Message protocol:
+ // what: get vs. set
+ // arg1: action -- register vs. disable
+ // arg2: get vs. set for the preceding request
+ private static class MyHandler extends Handler {
+ private static final int MESSAGE_GET_CALL_BARRING = 0;
+ private static final int MESSAGE_SET_CALL_BARRING = 1;
+
+ private final WeakReference<CallBarringEditPreference> mCallBarringEditPreference;
+
+ private MyHandler(CallBarringEditPreference callBarringEditPreference) {
+ mCallBarringEditPreference =
+ new WeakReference<CallBarringEditPreference>(callBarringEditPreference);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_GET_CALL_BARRING:
+ handleGetCallBarringResponse(msg);
+ break;
+ case MESSAGE_SET_CALL_BARRING:
+ handleSetCallBarringResponse(msg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Handle the response message for query CB status.
+ private void handleGetCallBarringResponse(Message msg) {
+ final CallBarringEditPreference pref = mCallBarringEditPreference.get();
+ if (pref == null) {
+ return;
+ }
+
+ if (DBG) {
+ Log.d(LOG_TAG, "handleGetCallBarringResponse: done");
+ }
+
+ AsyncResult ar = (AsyncResult) msg.obj;
+
+ if (msg.arg2 == MESSAGE_SET_CALL_BARRING) {
+ pref.mTcpListener.onFinished(pref, false);
+ } else {
+ pref.mTcpListener.onFinished(pref, true);
+ ImsPhone imsPhone = pref.mPhone != null
+ ? (ImsPhone) pref.mPhone.getImsPhone() : null;
+ if (!pref.mShowPassword && (imsPhone == null || !imsPhone.isUtEnabled())) {
+ // Re-enable password when rejected from NW and modem would perform CSFB
+ pref.mShowPassword = true;
+ if (DBG) {
+ Log.d(LOG_TAG,
+ "handleGetCallBarringResponse: mShowPassword changed for CSFB");
+ }
+ }
+ }
+
+ // Unsuccessful query for call barring.
+ if (ar.exception != null) {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleGetCallBarringResponse: ar.exception=" + ar.exception);
+ }
+ pref.mTcpListener.onException(pref, (CommandException) ar.exception);
+ } else {
+ if (ar.userObj instanceof Throwable) {
+ pref.mTcpListener.onError(pref, RESPONSE_ERROR);
+ }
+ int[] ints = (int[]) ar.result;
+ if (ints.length == 0) {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleGetCallBarringResponse: ar.result.length==0");
+ }
+ pref.setEnabled(false);
+ pref.mTcpListener.onError(pref, RESPONSE_ERROR);
+ } else {
+ pref.handleCallBarringResult(ints[0] != 0);
+ if (DBG) {
+ Log.d(LOG_TAG,
+ "handleGetCallBarringResponse: CB state successfully queried: "
+ + ints[0]);
+ }
+ }
+ }
+ // Update call barring status.
+ pref.updateSummaryText();
+ }
+
+ // Handle the response message for CB settings.
+ private void handleSetCallBarringResponse(Message msg) {
+ final CallBarringEditPreference pref = mCallBarringEditPreference.get();
+ if (pref == null) {
+ return;
+ }
+
+ AsyncResult ar = (AsyncResult) msg.obj;
+
+ if (ar.exception != null || ar.userObj instanceof Throwable) {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleSetCallBarringResponse: ar.exception=" + ar.exception);
+ }
+ }
+ if (DBG) {
+ Log.d(LOG_TAG, "handleSetCallBarringResponse: re-get call barring option");
+ }
+ pref.mPhone.getCallBarring(
+ pref.mFacility,
+ "",
+ obtainMessage(MESSAGE_GET_CALL_BARRING, 0, MESSAGE_SET_CALL_BARRING,
+ ar.exception),
+ 0);
+ }
+ }
+}
diff --git a/src/com/android/phone/GsmUmtsCallBarringOptions.java b/src/com/android/phone/GsmUmtsCallBarringOptions.java
new file mode 100644
index 0000000..4b875ee
--- /dev/null
+++ b/src/com/android/phone/GsmUmtsCallBarringOptions.java
@@ -0,0 +1,536 @@
+/*
+ * 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;
+
+import android.app.ActionBar;
+import android.app.Dialog;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.phone.settings.fdn.EditPinPreference;
+
+import java.util.ArrayList;
+
+/**
+ * Implements the preference to enable/disable calling barring options and
+ * the dialogs to change the passward.
+ */
+public class GsmUmtsCallBarringOptions extends TimeConsumingPreferenceActivity
+ implements EditPinPreference.OnPinEnteredListener {
+ private static final String LOG_TAG = "GsmUmtsCallBarringOptions";
+ private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+ // String keys for preference lookup
+ // Preference is handled solely in xml.
+ // Block all outgoing calls
+ private static final String BUTTON_BAOC_KEY = "button_baoc_key";
+ // Block all outgoing international calls
+ private static final String BUTTON_BAOIC_KEY = "button_baoic_key";
+ // Block all outgoing international roaming calls
+ private static final String BUTTON_BAOICxH_KEY = "button_baoicxh_key";
+ // Block all incoming calls
+ private static final String BUTTON_BAIC_KEY = "button_baic_key";
+ // Block all incoming international roaming calls
+ private static final String BUTTON_BAICr_KEY = "button_baicr_key";
+ // Disable all barring
+ private static final String BUTTON_BA_ALL_KEY = "button_ba_all_key";
+ // Change passward
+ private static final String BUTTON_BA_CHANGE_PW_KEY = "button_change_pw_key";
+
+ private static final String PW_CHANGE_STATE_KEY = "pin_change_state_key";
+ private static final String OLD_PW_KEY = "old_pw_key";
+ private static final String NEW_PW_KEY = "new_pw_key";
+ private static final String DIALOG_MESSAGE_KEY = "dialog_message_key";
+ private static final String DIALOG_PW_ENTRY_KEY = "dialog_pw_enter_key";
+ private static final String KEY_STATUS = "toggle";
+ private static final String PREFERENCE_ENABLED_KEY = "PREFERENCE_ENABLED";
+ private static final String PREFERENCE_SHOW_PASSWORD_KEY = "PREFERENCE_SHOW_PASSWORD";
+ private static final String SAVED_BEFORE_LOAD_COMPLETED_KEY = "PROGRESS_SHOWING";
+
+ private CallBarringEditPreference mButtonBAOC;
+ private CallBarringEditPreference mButtonBAOIC;
+ private CallBarringEditPreference mButtonBAOICxH;
+ private CallBarringEditPreference mButtonBAIC;
+ private CallBarringEditPreference mButtonBAICr;
+ private CallBarringDeselectAllPreference mButtonDisableAll;
+ private EditPinPreference mButtonChangePW;
+
+ // State variables
+ private int mPwChangeState;
+ private String mOldPassword;
+ private String mNewPassword;
+ private int mPwChangeDialogStrId;
+
+ private static final int PW_CHANGE_OLD = 0;
+ private static final int PW_CHANGE_NEW = 1;
+ private static final int PW_CHANGE_REENTER = 2;
+
+ private static final int BUSY_READING_DIALOG = 100;
+ private static final int BUSY_SAVING_DIALOG = 200;
+
+ // Password change complete event
+ private static final int EVENT_PW_CHANGE_COMPLETE = 100;
+ // Disable all complete event
+ private static final int EVENT_DISABLE_ALL_COMPLETE = 200;
+
+ private static final int PW_LENGTH = 4;
+
+ private Phone mPhone;
+ private ArrayList<CallBarringEditPreference> mPreferences =
+ new ArrayList<CallBarringEditPreference>();
+ private int mInitIndex = 0;
+ private boolean mFirstResume;
+ private Bundle mIcicle;
+
+ private SubscriptionInfoHelper mSubscriptionInfoHelper;
+ private Dialog mProgressDialog;
+
+ @Override
+ public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
+ if (preference == mButtonChangePW) {
+ updatePWChangeState(positiveResult);
+ } else if (preference == mButtonDisableAll) {
+ disableAllBarring(positiveResult);
+ }
+ }
+
+ /**
+ * Display a toast for message.
+ */
+ private void displayMessage(int strId) {
+ Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
+ }
+
+ /**
+ * Attempt to disable all for call barring settings.
+ */
+ private void disableAllBarring(boolean positiveResult) {
+ if (!positiveResult) {
+ // Return on cancel
+ return;
+ }
+
+ String password = null;
+ if (mButtonDisableAll.isPasswordShown()) {
+ password = mButtonDisableAll.getText();
+ // Validate the length of password first, before submitting it to the
+ // RIL for CB disable.
+ if (!validatePassword(password)) {
+ mButtonDisableAll.setText("");
+ displayMessage(R.string.call_barring_right_pwd_number);
+ return;
+ }
+ }
+
+ // Submit the disable all request
+ mButtonDisableAll.setText("");
+ Message onComplete = mHandler.obtainMessage(EVENT_DISABLE_ALL_COMPLETE);
+ mPhone.setCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, false, password, onComplete, 0);
+ this.onStarted(mButtonDisableAll, false);
+ }
+
+ /**
+ * Attempt to change the password for call barring settings.
+ */
+ private void updatePWChangeState(boolean positiveResult) {
+ if (!positiveResult) {
+ // Reset the state on cancel
+ resetPwChangeState();
+ return;
+ }
+
+ // Progress through the dialog states, generally in this order:
+ // 1. Enter old password
+ // 2. Enter new password
+ // 3. Re-Enter new password
+ // In general, if any invalid entries are made, the dialog re-
+ // appears with text to indicate what the issue is.
+ switch (mPwChangeState) {
+ case PW_CHANGE_OLD:
+ mOldPassword = mButtonChangePW.getText();
+ mButtonChangePW.setText("");
+ if (validatePassword(mOldPassword)) {
+ mPwChangeState = PW_CHANGE_NEW;
+ displayPwChangeDialog();
+ } else {
+ displayPwChangeDialog(R.string.call_barring_right_pwd_number, true);
+ }
+ break;
+ case PW_CHANGE_NEW:
+ mNewPassword = mButtonChangePW.getText();
+ mButtonChangePW.setText("");
+ if (validatePassword(mNewPassword)) {
+ mPwChangeState = PW_CHANGE_REENTER;
+ displayPwChangeDialog();
+ } else {
+ displayPwChangeDialog(R.string.call_barring_right_pwd_number, true);
+ }
+ break;
+ case PW_CHANGE_REENTER:
+ // If the re-entered password is not valid, display a message
+ // and reset the state.
+ if (!mNewPassword.equals(mButtonChangePW.getText())) {
+ mPwChangeState = PW_CHANGE_NEW;
+ mButtonChangePW.setText("");
+ displayPwChangeDialog(R.string.call_barring_pwd_not_match, true);
+ } else {
+ // If the password is valid, then submit the change password request
+ mButtonChangePW.setText("");
+ Message onComplete = mHandler.obtainMessage(EVENT_PW_CHANGE_COMPLETE);
+ ((GsmCdmaPhone) mPhone).changeCallBarringPassword(
+ CommandsInterface.CB_FACILITY_BA_ALL,
+ mOldPassword, mNewPassword, onComplete);
+ this.onStarted(mButtonChangePW, false);
+ }
+ break;
+ default:
+ if (DBG) {
+ Log.d(LOG_TAG, "updatePWChangeState: Unknown password change state: "
+ + mPwChangeState);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Handler for asynchronous replies from the framework layer.
+ */
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ switch (msg.what) {
+ // Handle the response message for password change from the framework layer.
+ case EVENT_PW_CHANGE_COMPLETE: {
+ onFinished(mButtonChangePW, false);
+ // Unsuccessful change, display a toast to user with failure reason.
+ if (ar.exception != null) {
+ if (DBG) {
+ Log.d(LOG_TAG,
+ "change password for call barring failed with exception: "
+ + ar.exception);
+ }
+ onException(mButtonChangePW, (CommandException) ar.exception);
+ mButtonChangePW.setEnabled(true);
+ } else if (ar.userObj instanceof Throwable) {
+ onError(mButtonChangePW, RESPONSE_ERROR);
+ } else {
+ // Successful change.
+ displayMessage(R.string.call_barring_change_pwd_success);
+ }
+ resetPwChangeState();
+ break;
+ }
+ // When disabling all call barring, either fail and display a toast,
+ // or just update the UI.
+ case EVENT_DISABLE_ALL_COMPLETE: {
+ onFinished(mButtonDisableAll, false);
+ if (ar.exception != null) {
+ if (DBG) {
+ Log.d(LOG_TAG, "can not disable all call barring with exception: "
+ + ar.exception);
+ }
+ onException(mButtonDisableAll, (CommandException) ar.exception);
+ mButtonDisableAll.setEnabled(true);
+ } else if (ar.userObj instanceof Throwable) {
+ onError(mButtonDisableAll, RESPONSE_ERROR);
+ } else {
+ // Reset to normal behaviour on successful change.
+ displayMessage(R.string.call_barring_deactivate_success);
+ resetCallBarringPrefState(false);
+ }
+ break;
+ }
+ default: {
+ if (DBG) {
+ Log.d(LOG_TAG, "Unknown message id: " + msg.what);
+ }
+ break;
+ }
+ }
+ }
+ };
+
+ /**
+ * The next two functions are for updating the message field on the dialog.
+ */
+ private void displayPwChangeDialog() {
+ displayPwChangeDialog(0, true);
+ }
+
+ private void displayPwChangeDialog(int strId, boolean shouldDisplay) {
+ int msgId = 0;
+ switch (mPwChangeState) {
+ case PW_CHANGE_OLD:
+ msgId = R.string.call_barring_old_pwd;
+ break;
+ case PW_CHANGE_NEW:
+ msgId = R.string.call_barring_new_pwd;
+ break;
+ case PW_CHANGE_REENTER:
+ msgId = R.string.call_barring_confirm_pwd;
+ break;
+ default:
+ break;
+ }
+
+ // Append the note/additional message, if needed.
+ if (strId != 0) {
+ mButtonChangePW.setDialogMessage(getText(msgId) + "\n" + getText(strId));
+ } else {
+ mButtonChangePW.setDialogMessage(msgId);
+ }
+
+ // Only display if requested.
+ if (shouldDisplay) {
+ mButtonChangePW.showPinDialog();
+ }
+ mPwChangeDialogStrId = strId;
+ }
+
+ /**
+ * Reset the state of the password change dialog.
+ */
+ private void resetPwChangeState() {
+ mPwChangeState = PW_CHANGE_OLD;
+ displayPwChangeDialog(0, false);
+ mOldPassword = "";
+ mNewPassword = "";
+ }
+
+ /**
+ * Reset the state of the all call barring setting to disable.
+ */
+ private void resetCallBarringPrefState(boolean enable) {
+ for (CallBarringEditPreference pref : mPreferences) {
+ pref.mIsActivated = enable;
+ pref.updateSummaryText();
+ }
+ }
+
+ /**
+ * Validate the password entry.
+ *
+ * @param password This is the password to validate
+ */
+ private boolean validatePassword(String password) {
+ return password != null && password.length() == PW_LENGTH;
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ if (DBG) {
+ Log.d(LOG_TAG, "onCreate, reading callbarring_options.xml file");
+ }
+ addPreferencesFromResource(R.xml.callbarring_options);
+
+ mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
+ mPhone = mSubscriptionInfoHelper.getPhone();
+ if (DBG) {
+ Log.d(LOG_TAG, "onCreate, reading callbarring_options.xml file finished!");
+ }
+
+ // Get UI object references
+ PreferenceScreen prefSet = getPreferenceScreen();
+ mButtonBAOC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOC_KEY);
+ mButtonBAOIC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOIC_KEY);
+ mButtonBAOICxH = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOICxH_KEY);
+ mButtonBAIC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAIC_KEY);
+ mButtonBAICr = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAICr_KEY);
+ mButtonDisableAll = (CallBarringDeselectAllPreference)
+ prefSet.findPreference(BUTTON_BA_ALL_KEY);
+ mButtonChangePW = (EditPinPreference) prefSet.findPreference(BUTTON_BA_CHANGE_PW_KEY);
+
+ // Assign click listener and update state
+ mButtonBAOC.setOnPinEnteredListener(this);
+ mButtonBAOIC.setOnPinEnteredListener(this);
+ mButtonBAOICxH.setOnPinEnteredListener(this);
+ mButtonBAIC.setOnPinEnteredListener(this);
+ mButtonBAICr.setOnPinEnteredListener(this);
+ mButtonDisableAll.setOnPinEnteredListener(this);
+ mButtonChangePW.setOnPinEnteredListener(this);
+
+ // Store CallBarringEditPreferencence objects in array list.
+ mPreferences.add(mButtonBAOC);
+ mPreferences.add(mButtonBAOIC);
+ mPreferences.add(mButtonBAOICxH);
+ mPreferences.add(mButtonBAIC);
+ mPreferences.add(mButtonBAICr);
+
+ // Find out if password is currently used.
+ boolean usePassword = true;
+ boolean useDisableaAll = true;
+
+ ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
+ if (imsPhone != null
+ && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+ || imsPhone.isUtEnabled())) {
+ usePassword = false;
+ useDisableaAll = false;
+ }
+
+ // Find out if the sim card is ready.
+ boolean isSimReady = TelephonyManager.from(this).getSimState(
+ SubscriptionManager.getSlotIndex(mPhone.getSubId()))
+ == TelephonyManager.SIM_STATE_READY;
+
+ // Deactivate all option is unavailable when sim card is not ready or Ut is enabled.
+ if (isSimReady && useDisableaAll) {
+ mButtonDisableAll.setEnabled(true);
+ mButtonDisableAll.init(mPhone);
+ } else {
+ mButtonDisableAll.setEnabled(false);
+ }
+
+ // Change password option is unavailable when sim card is not ready or when the password is
+ // not used.
+ if (isSimReady && usePassword) {
+ mButtonChangePW.setEnabled(true);
+ } else {
+ mButtonChangePW.setEnabled(false);
+ mButtonChangePW.setSummary(R.string.call_barring_change_pwd_description_disabled);
+ }
+
+ // Wait to do the initialization until onResume so that the TimeConsumingPreferenceActivity
+ // dialog can display as it relies on onResume / onPause to maintain its foreground state.
+ mFirstResume = true;
+ mIcicle = icicle;
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ // android.R.id.home will be triggered in onOptionsItemSelected()
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ if (mIcicle != null && !mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) {
+ if (DBG) {
+ Log.d(LOG_TAG, "restore stored states");
+ }
+ mInitIndex = mPreferences.size();
+
+ for (CallBarringEditPreference pref : mPreferences) {
+ Bundle bundle = mIcicle.getParcelable(pref.getKey());
+ if (bundle != null) {
+ pref.handleCallBarringResult(bundle.getBoolean(KEY_STATUS));
+ pref.init(this, true, mPhone);
+ pref.setEnabled(bundle.getBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled()));
+ pref.setInputMethodNeeded(bundle.getBoolean(PREFERENCE_SHOW_PASSWORD_KEY,
+ pref.needInputMethod()));
+ }
+ }
+ mPwChangeState = mIcicle.getInt(PW_CHANGE_STATE_KEY);
+ mOldPassword = mIcicle.getString(OLD_PW_KEY);
+ mNewPassword = mIcicle.getString(NEW_PW_KEY);
+ displayPwChangeDialog(mIcicle.getInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId), false);
+ mButtonChangePW.setText(mIcicle.getString(DIALOG_PW_ENTRY_KEY));
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (mFirstResume) {
+ if (mIcicle == null || mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) {
+ if (DBG) {
+ Log.d(LOG_TAG, "onResume: start to init ");
+ }
+ resetPwChangeState();
+ mPreferences.get(mInitIndex).init(this, false, mPhone);
+
+ // Request removing BUSY_SAVING_DIALOG because reading is restarted.
+ // (If it doesn't exist, nothing happen.)
+ removeDialog(BUSY_SAVING_DIALOG);
+ }
+ mFirstResume = false;
+ mIcicle = null;
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ for (CallBarringEditPreference pref : mPreferences) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(KEY_STATUS, pref.mIsActivated);
+ bundle.putBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled());
+ bundle.putBoolean(PREFERENCE_SHOW_PASSWORD_KEY, pref.needInputMethod());
+ outState.putParcelable(pref.getKey(), bundle);
+ }
+ outState.putInt(PW_CHANGE_STATE_KEY, mPwChangeState);
+ outState.putString(OLD_PW_KEY, mOldPassword);
+ outState.putString(NEW_PW_KEY, mNewPassword);
+ outState.putInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId);
+ outState.putString(DIALOG_PW_ENTRY_KEY, mButtonChangePW.getText());
+
+ outState.putBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY,
+ mProgressDialog != null && mProgressDialog.isShowing());
+ }
+
+ /**
+ * Finish initialization of this preference and start next.
+ *
+ * @param preference The preference.
+ * @param reading If true to dismiss the busy reading dialog,
+ * false to dismiss the busy saving dialog.
+ */
+ public void onFinished(Preference preference, boolean reading) {
+ if (mInitIndex < mPreferences.size() - 1 && !isFinishing()) {
+ mInitIndex++;
+ mPreferences.get(mInitIndex).init(this, false, mPhone);
+ }
+ super.onFinished(preference, reading);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int itemId = item.getItemId();
+ if (itemId == android.R.id.home) {
+ CallFeaturesSetting.goUpToTopLevelSetting(this, mSubscriptionInfoHelper);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
+ super.onPrepareDialog(id, dialog, args);
+ if (id == BUSY_READING_DIALOG || id == BUSY_SAVING_DIALOG) {
+ // For onSaveInstanceState, treat the SAVING dialog as the same as the READING. As
+ // the result, if the activity is recreated while waiting for SAVING, it starts reading
+ // all the newest data.
+ mProgressDialog = dialog;
+ }
+ }
+}
diff --git a/src/com/android/phone/GsmUmtsCallOptions.java b/src/com/android/phone/GsmUmtsCallOptions.java
index 419e72c..3b27d28 100644
--- a/src/com/android/phone/GsmUmtsCallOptions.java
+++ b/src/com/android/phone/GsmUmtsCallOptions.java
@@ -17,10 +17,12 @@
package com.android.phone;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
import android.view.MenuItem;
import com.android.internal.telephony.Phone;
@@ -31,6 +33,7 @@
private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+ private static final String CALL_BARRING_KEY = "call_barring_key";
private static final String ADDITIONAL_GSM_SETTINGS_KEY = "additional_gsm_call_settings_key";
@Override
@@ -68,5 +71,18 @@
prefScreen.findPreference(ADDITIONAL_GSM_SETTINGS_KEY);
additionalGsmSettingsPref.setIntent(
subInfoHelper.getIntent(GsmUmtsAdditionalCallOptions.class));
+
+ Preference callBarringPref = prefScreen.findPreference(CALL_BARRING_KEY);
+ PersistableBundle b = null;
+ if (subInfoHelper.hasSubId()) {
+ b = PhoneGlobals.getInstance().getCarrierConfigForSubId(subInfoHelper.getSubId());
+ } else {
+ b = PhoneGlobals.getInstance().getCarrierConfig();
+ }
+ if (b != null && b.getBoolean(CarrierConfigManager.KEY_CALL_BARRING_VISIBILITY_BOOL)) {
+ callBarringPref.setIntent(subInfoHelper.getIntent(GsmUmtsCallBarringOptions.class));
+ } else {
+ prefScreen.removePreference(callBarringPref);
+ }
}
}
diff --git a/src/com/android/phone/LocationAccessPolicy.java b/src/com/android/phone/LocationAccessPolicy.java
deleted file mode 100644
index 6f2a5ec..0000000
--- a/src/com/android/phone/LocationAccessPolicy.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.os.Build;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import java.util.List;
-
-/**
- * Helper for performing location access checks.
- */
-final class LocationAccessPolicy {
-
- private LocationAccessPolicy() {
- /* do nothing - hide ctor */
- }
-
- /**
- * API to determine if the caller has permissions to get cell location.
- *
- * @param pkgName Package name of the application requesting access
- * @param uid The uid of the package
- * @param message Message to add to the exception if no location permission
- * @return boolean true or false if permissions is granted
- */
- static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
- int uid, String message) throws SecurityException {
- context.getSystemService(AppOpsManager.class).checkPackage(uid, pkgName);
- // We always require the location permission and also require the
- // location mode to be on for non-legacy apps. Legacy apps are
- // required to be in the foreground to at least mitigate the case
- // where a legacy app the user is not using tracks their location.
-
- // Grating ACCESS_FINE_LOCATION to an app automatically grants it ACCESS_COARSE_LOCATION.
- context.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION, message);
- final int opCode = AppOpsManager.permissionToOpCode(
- Manifest.permission.ACCESS_COARSE_LOCATION);
- if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
- .noteOp(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
- return false;
- }
- if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
- && !isLegacyForeground(context, pkgName)) {
- return false;
- }
- // If the user or profile is current, permission is granted.
- // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
- return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
- }
-
- private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
- return Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId)
- != Settings.Secure.LOCATION_MODE_OFF;
- }
-
- private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName) {
- return isLegacyVersion(context, pkgName) && isForegroundApp(context, pkgName);
- }
-
- private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
- try {
- if (context.getPackageManager().getApplicationInfo(pkgName, 0)
- .targetSdkVersion <= Build.VERSION_CODES.O) {
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- // In case of exception, assume known app (more strict checking)
- // Note: This case will never happen since checkPackage is
- // called to verify validity before checking app's version.
- }
- return false;
- }
-
- private static boolean isForegroundApp(@NonNull Context context, @NonNull String pkgName) {
- final ActivityManager am = context.getSystemService(ActivityManager.class);
- final List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
- if (!tasks.isEmpty()) {
- return pkgName.equals(tasks.get(0).topActivity.getPackageName());
- }
- return false;
- }
-
- private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
- return context.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- private static boolean isCurrentProfile(@NonNull Context context, int uid) {
- final int currentUser = ActivityManager.getCurrentUser();
- final int callingUserId = UserHandle.getUserId(uid);
- if (callingUserId == currentUser) {
- return true;
- } else {
- List<UserInfo> userProfiles = context.getSystemService(
- UserManager.class).getProfiles(currentUser);
- for (UserInfo user: userProfiles) {
- if (user.id == callingUserId) {
- return true;
- }
- }
- }
- return false;
- }
-}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index d658a11..0c76446 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -55,6 +55,7 @@
import android.telephony.CellInfo;
import android.telephony.ClientRequestStats;
import android.telephony.IccOpenLogicalChannelResponse;
+import android.telephony.LocationAccessPolicy;
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.NetworkScanRequest;
@@ -1653,8 +1654,10 @@
@Override
public Bundle getCellLocation(String callingPackage) {
+ mPhone.getContext().getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
- callingPackage, Binder.getCallingUid(), "getCellLocation")) {
+ callingPackage, Binder.getCallingUid(),Binder.getCallingPid())) {
return null;
}
@@ -1721,8 +1724,10 @@
@Override
@SuppressWarnings("unchecked")
public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
+ mPhone.getContext().getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
- callingPackage, Binder.getCallingUid(), "getNeighboringCellInfo")) {
+ callingPackage, Binder.getCallingUid(), Binder.getCallingPid())) {
return null;
}
@@ -1749,8 +1754,10 @@
@Override
public List<CellInfo> getAllCellInfo(String callingPackage) {
+ mPhone.getContext().getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
- callingPackage, Binder.getCallingUid(), "getAllCellInfo")) {
+ callingPackage, Binder.getCallingUid(), Binder.getCallingPid())) {
return null;
}
diff --git a/src/com/android/phone/settings/fdn/EditPinPreference.java b/src/com/android/phone/settings/fdn/EditPinPreference.java
index eaa3507..42fc418 100644
--- a/src/com/android/phone/settings/fdn/EditPinPreference.java
+++ b/src/com/android/phone/settings/fdn/EditPinPreference.java
@@ -39,7 +39,17 @@
private boolean shouldHideButtons;
- interface OnPinEnteredListener {
+ /**
+ * Interface definition for a callback to be invoked when the PIN is entered.
+ */
+ public interface OnPinEnteredListener {
+ /**
+ * Called when the dialog of {@link #EditPinPreference} is dismissed.
+ *
+ * @param preference the specified {@link #EditPinPreference}
+ * @param positiveResult Whether the positive button was clicked (true), or
+ * the negative button was clicked or the dialog was canceled (false).
+ */
void onPinEntered(EditPinPreference preference, boolean positiveResult);
}
diff --git a/src/com/android/services/telephony/EmergencyTonePlayer.java b/src/com/android/services/telephony/EmergencyTonePlayer.java
index aaec24f..a21489d 100644
--- a/src/com/android/services/telephony/EmergencyTonePlayer.java
+++ b/src/com/android/services/telephony/EmergencyTonePlayer.java
@@ -67,11 +67,7 @@
startVibrate();
break;
case EMERGENCY_TONE_ALERT:
- // Only start if we are not in silent mode.
- int ringerMode = mAudioManager.getRingerMode();
- if (ringerMode == AudioManager.RINGER_MODE_NORMAL) {
- startAlert();
- }
+ startAlert();
break;
case EMERGENCY_TONE_OFF:
// nothing;
diff --git a/src/com/android/services/telephony/GsmConnection.java b/src/com/android/services/telephony/GsmConnection.java
index 0a58fba..999c6f5 100644
--- a/src/com/android/services/telephony/GsmConnection.java
+++ b/src/com/android/services/telephony/GsmConnection.java
@@ -81,12 +81,6 @@
}
}
- // For GSM connections, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN should be applied whenever
- // PROPERTY_IS_DOWNGRADED_CONFERENCE is true.
- if ((getConnectionProperties() & PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0) {
- capabilities |= CAPABILITY_CONFERENCE_HAS_NO_CHILDREN;
- }
-
return capabilities;
}
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index d523787..72184c4 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -910,7 +910,6 @@
mConferenceHost.isOutgoingCall());
// This is a newly created conference connection as a result of SRVCC
c.setConferenceSupported(true);
- c.addCapability(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
c.setConnectionProperties(
c.getConnectionProperties() | Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
c.updateState();
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 93d7f87..87d8e34 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -227,6 +227,13 @@
isHandoverFromSupported);
}
+ final boolean isTelephonyAudioDeviceSupported = mContext.getResources().getBoolean(
+ R.bool.config_support_telephony_audio_device);
+ if (isTelephonyAudioDeviceSupported && !isEmergency
+ && isCarrierUseCallRecordingTone()) {
+ extras.putBoolean(PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, true);
+ }
+
boolean isDeviceRttSupported = mContext.getResources().getBoolean(
R.bool.config_support_rtt);
if (isDeviceRttSupported && isCarrierRttSupported()) {
@@ -444,6 +451,18 @@
}
/**
+ * Determines from carrier config whether the carrier requires the use of a call recording
+ * tone.
+ *
+ * @return {@code true} if a call recording tone should be used, {@code false} otherwise.
+ */
+ private boolean isCarrierUseCallRecordingTone() {
+ PersistableBundle b =
+ PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+ return b.getBoolean(CarrierConfigManager.KEY_PLAY_CALL_RECORDING_TONE_BOOL);
+ }
+
+ /**
* Where a device supports instant lettering and call subjects, retrieves the necessary
* PhoneAccount extras for those features.
*
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index b82c202..36411d7 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -33,6 +33,7 @@
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telephony.CarrierConfigManager;
+import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallProfile;
@@ -443,6 +444,12 @@
sendRttInitiationFailure(status);
}
}
+
+ @Override
+ public void onDisconnect(int cause) {
+ Log.i(this, "onDisconnect: cause=%s", DisconnectCause.toString(cause));
+ mHandler.obtainMessage(MSG_DISCONNECT);
+ }
};
protected com.android.internal.telephony.Connection mOriginalConnection;
@@ -899,7 +906,6 @@
getPhone().registerForHandoverStateChanged(
mHandler, MSG_HANDOVER_STATE_CHANGED, null);
getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
- getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null);
getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
getPhone().registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
getPhone().registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null);
@@ -1050,6 +1056,8 @@
b != null && b.getBoolean(CarrierConfigManager.KEY_WIFI_CALLS_CAN_BE_HD_AUDIO);
boolean canVideoCallsBeHdAudio =
b != null && b.getBoolean(CarrierConfigManager.KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO);
+ boolean canGsmCdmaCallsBeHdAudio =
+ b != null && b.getBoolean(CarrierConfigManager.KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO);
boolean shouldDisplayHdAudio =
b != null && b.getBoolean(CarrierConfigManager.KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL);
@@ -1057,6 +1065,10 @@
return false;
}
+ if (isGsmCdmaConnection() && !canGsmCdmaCallsBeHdAudio) {
+ return false;
+ }
+
if (isVideoCall && !canVideoCallsBeHdAudio) {
return false;
}
@@ -1339,7 +1351,8 @@
newState = mOriginalConnection.getState();
}
int cause = mOriginalConnection.getDisconnectCause();
- Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState, this);
+ Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState,
+ getTelecomCallId());
if (mConnectionState != newState) {
mConnectionState = newState;
@@ -1720,6 +1733,25 @@
}
/**
+ * Whether the original connection is an GSM/CDMA connection.
+ * @return {@code True} if the original connection is an GSM/CDMA connection, {@code false}
+ * otherwise.
+ */
+ protected boolean isGsmCdmaConnection() {
+ Phone phone = getPhone();
+ if (phone != null) {
+ switch (phone.getPhoneType()) {
+ case PhoneConstants.PHONE_TYPE_GSM:
+ case PhoneConstants.PHONE_TYPE_CDMA:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
* Whether the original connection was ever an IMS connection, either before or now.
* @return {@code True} if the original connection was ever an IMS connection, {@code false}
* otherwise.
diff --git a/testapps/TelephonyRegistryTestApp/Android.mk b/testapps/TelephonyRegistryTestApp/Android.mk
new file mode 100644
index 0000000..ed1f2a3
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+src_dirs := src
+res_dirs := res
+
+LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
+
+LOCAL_PACKAGE_NAME := TelephonyRegistryTestApp
+
+LOCAL_CERTIFICATE := platform
+LOCAL_MODULE_TAGS := tests
+#LOCAL_MODULE_TAGS := debug
+
+include $(BUILD_PACKAGE)
diff --git a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..5f19509
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.phone.testapps.telephonyregistry">
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <application android:label="TelephonyRegistryTestApp">
+ <activity
+ android:name=".TelephonyRegistryTestApp"
+ android:label="TelephonyRegistryTestApp">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
+
diff --git a/testapps/TelephonyRegistryTestApp/res/layout/activity_main.xml b/testapps/TelephonyRegistryTestApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..405efe2
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/res/layout/activity_main.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/events">
+ </LinearLayout>
+ </ScrollView>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/queryCellLocationButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_row="0"
+ android:layout_column="0"
+ android:text="@string/query_cell_location_button" />
+ <Button
+ android:id="@+id/registerButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_row="0"
+ android:layout_column="0"
+ android:text="@string/register_button" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/testapps/TelephonyRegistryTestApp/res/values/donottranslate_strings.xml b/testapps/TelephonyRegistryTestApp/res/values/donottranslate_strings.xml
new file mode 100644
index 0000000..897b83e
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/res/values/donottranslate_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<resources>
+ <string name="register_button">Register</string>
+ <string name="query_cell_location_button">Query</string>
+</resources>
\ No newline at end of file
diff --git a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
new file mode 100644
index 0000000..e423619
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 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.testapps.telephonyregistry;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.Bundle;
+import android.provider.Telephony;
+import android.telephony.CellInfo;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.util.SparseArray;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TelephonyRegistryTestApp extends Activity {
+ private TelephonyManager telephonyManager;
+ private NotificationManager notificationManager;
+ private int mSelectedEvents = 0;
+ private static final String NOTIFICATION_CHANNEL = "registryUpdate";
+
+ private static final SparseArray<String> EVENTS = new SparseArray<String>() {{
+ put(PhoneStateListener.LISTEN_SERVICE_STATE, "SERVICE_STATE");
+ put(PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR, "MESSAGE_WAITING_INDICATOR");
+ put(PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR, "CALL_FORWARDING_INDICATOR");
+ put(PhoneStateListener.LISTEN_CELL_LOCATION, "CELL_LOCATION");
+ put(PhoneStateListener.LISTEN_CALL_STATE, "CALL_STATE");
+ put(PhoneStateListener.LISTEN_DATA_CONNECTION_STATE, "DATA_CONNECTION_STATE");
+ put(PhoneStateListener.LISTEN_DATA_ACTIVITY, "DATA_ACTIVITY");
+ put(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS, "SIGNAL_STRENGTHS");
+ put(PhoneStateListener.LISTEN_OTASP_CHANGED, "OTASP_CHANGED");
+ put(PhoneStateListener.LISTEN_CELL_INFO, "CELL_INFO");
+ put(PhoneStateListener.LISTEN_PRECISE_CALL_STATE, "PRECISE_CALL_STATE");
+ put(PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE,
+ "PRECISE_DATA_CONNECTION_STATE");
+ put(PhoneStateListener.LISTEN_VOLTE_STATE, "VOLTE_STATE");
+ put(PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE, "CARRIER_NETWORK_CHANGE");
+ put(PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE, "VOICE_ACTIVATION_STATE");
+ put(PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE, "DATA_ACTIVATION_STATE");
+ }};
+
+ private final PhoneStateListener phoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onCellLocationChanged(CellLocation location) {
+ notify("onCellLocationChanged", location);
+ }
+
+ @Override
+ public void onCellInfoChanged(List<CellInfo> cellInfo) {
+ notify("onCellInfoChanged", cellInfo);
+ }
+
+ private void notify(String method, Object data) {
+ Notification.Builder builder = new Notification.Builder(TelephonyRegistryTestApp.this,
+ NOTIFICATION_CHANNEL);
+ Notification notification = builder.setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setContentTitle("Registry update: " + method)
+ .setContentText(data == null ? "null" : data.toString())
+ .build();
+ notificationManager.notify(0, notification);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ telephonyManager = getSystemService(TelephonyManager.class);
+
+ LinearLayout eventContainer = (LinearLayout) findViewById(R.id.events);
+ for (int i = 0; i < EVENTS.size(); i++) {
+ CheckBox box = new CheckBox(this);
+ box.setText(EVENTS.valueAt(i));
+ final int eventCode = EVENTS.keyAt(i);
+ box.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (buttonView.isChecked()) {
+ mSelectedEvents |= eventCode;
+ } else {
+ mSelectedEvents &= ~eventCode;
+ }
+ });
+ eventContainer.addView(box);
+ }
+
+ Button registerButton = (Button) findViewById(R.id.registerButton);
+ registerButton.setOnClickListener(v ->
+ telephonyManager.listen(phoneStateListener, mSelectedEvents));
+
+ Button queryCellLocationButton = findViewById(R.id.queryCellLocationButton);
+ queryCellLocationButton.setOnClickListener(v -> {
+ List<CellInfo> cellInfos = telephonyManager.getAllCellInfo();
+ String cellInfoText;
+ if (cellInfos == null || cellInfos.size() == 0) {
+ cellInfoText = "null";
+ } else {
+ cellInfoText = cellInfos.stream().map(CellInfo::toString)
+ .collect(Collectors.joining(","));
+ }
+ Toast.makeText(TelephonyRegistryTestApp.this, "queryCellInfo: " + cellInfoText,
+ Toast.LENGTH_SHORT).show();
+ });
+
+ notificationManager = getSystemService(NotificationManager.class);
+ NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL,
+ "Telephony Registry updates", NotificationManager.IMPORTANCE_HIGH);
+ notificationManager.createNotificationChannel(channel);
+ }
+}