diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 63f1df550..cecb879bd 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -23,6 +23,7 @@
+
+
+ Xestión de llamaes
+ Teléfonu
+ Llamada perdida
+ Llamaes perdíes
+ Llamada desconectada
+ Llamada en segundu planu
+ «%s» dexó de responder
+
+ Llamada col audiu desactiváu.
+ Altavoz habilitáu.
+ Nun puedo falar. ¿Qué pasó?
+ Llámote de siguío.
+ Llámote dempués.
+ Nun puedo falar. ¿Llámesme dempués?
+ Rempuestes rápides
+ Pa facer una llamada, introduz un númberu válidu.
+ Nesti momentu nun se pue amestar la llamada.
+ Nun s\'atroxó nengún númberu de buzón de voz na tarxeta SIM.
+ Predeterminar
+ Encaboxar
+ Predeterminar
+ Encaboxar
+ Desbloquiar
+ Bloquiar
+ Nun ye posible bloquiar el númberu d\'emerxencia.
+ Q Mobile
+ Market Wireless
+ Sonoma Circles Talk Plus
+ Bay Voice Chat Pro
+ Account with Q Mobile
+ Account with Market Wireless
+ Talk to everyone in your Circles!
+ Chat with Chat Network users
+ RESTRICTED
+ Llamaes perdíes
+ Llamaes en segundu planu
+ Llamaes desconectaes
+
diff --git a/res/values-cy/strings.xml b/res/values-cy/strings.xml
new file mode 100644
index 000000000..d689c6cf5
--- /dev/null
+++ b/res/values-cy/strings.xml
@@ -0,0 +1,134 @@
+
+
+
+ Rheolaeth Galwadau
+ Ffôn
+ Anhysybys
+ Galwad wedi ei methu
+ Galwad gwaith wedi\'i methu
+ Galwadau wedi eu methu
+ %s galwad wedi eu methu
+ Wedi methu galwad gan %s
+ Galw nôl
+ Neges
+ Galwad wedi\'i datgysylltu
+ Datgysylltwyd yr alwad â %s am fod galwad brys wedi ei gwneud.
+ Cafodd dy alwad ei datgysylltu am fod galwad brys wedi ei gwneud.
+
+ Galwad yn y cefndir
+ Mae %s wedi gosod galwad yn
+ y cefndir. Gall fod yr ap hwn yn cael at ac yn chwarae sain dros yr alwad.
+
+ Stopiodd %s ymateb
+
+ Defnyddiodd dy alwad yr ap ffôn daeth gyda dy ddyfais
+
+ Galwad wedi\'i distewi.
+ Seinydd uchel wedi\'i alluogi.
+ Methu siarad nawr. Be sy\'?
+ Mi alwa\'i nôl yn fuan.
+ Mi alwa\'i nôl yn hwyrach.
+ Methu siarad nawr. Galw fi\'n hwyrach?
+ Atebion cyflym
+ Golygu atebion cyflym
+ Ateb cyflym
+ Anfonwyd neges at %s.
+ Methwyd ag anfon y neges at %s.
+ Cyfrifon galw
+ Dim ond galwadau brys sydd yn bosib.
+ Ni all yr ap hwn wneud galwadau allan heb ganiatâd y Ffôn.
+ I wneud galwad, rho rif dilys.
+ Methu ag ychwanegu\'r alwad ar hyn o bryd.
+ Rhif lleisbost ar goll
+ Does dim rhif ar gyfer lleisbost wedi ei gadw ar y cerdyn SIM.
+ Ychwanegu rhif
+ Gosod %s fel dy ap Ffôn arferol?
+ Gosod fel Rhagosodiad
+ Diddymu
+ Bydd %s yn medru gwneud a rheoli pob agwedd o alwadau. Dylid ond osod ap wyt ti\'n ymddiried ynddo fel dy ap Ffôn diofyn.
+ Gosod %s fel dy ap arferol i sgrinio galwadau?
+ Ni fydd %s bellach yn medru sgrinio galwadau.
+ Bydd %s yn medru gweld gwybodaeth am alwadau na sydd yn dy gysylltiadau ac yn medru rhwystro\u2019r galwadau hyn. Dylid ond defnyddio ap rwyt yn ymddiried ynddynt fel dy ap sgrinio galwadau.
+ Gosod fel Rhagosodiad
+ Diddymu
+ Rhifau wedi eu rhwystro
+ Ni fyddi\'n derbyn galwadau na negesuon testun oddi wrth rifau wedi\'u rhwystro.
+ Ychwanegu rhif
+ Dadrwystro %1$s?
+ Dadrwystro
+ Rhwystro galwadau a negesuon testun oddi wrth
+ Rhif ffôn
+ Rhwystro
+ Dim ond perchennog y ddyfais gall gweld a rheoli rhifau wedi eu rhwystro.
+ Dadrwystro
+ Rhwystro wedi ei diffodd dros dro
+ Ar ôl deialu neu anfon neges at rif argyfwng, mae rhwystro yn cael ei diffodd i sicrhau bod modd i\'r gwasanaethau brys cysylltu â thi.
+ Ail-alluogi nawr
+ %1$s wedi\'i rwystro
+ %1$s wedi\'i dadrwystro
+ Amhosib rhwystro rhif argyfwng.
+ Mae %1$s eisoes wedi ei rwystro.
+ Q Mobile
+ Market Wireless
+ Sonoma Circles Talk Plus
+ Bay Voice Chat Pro
+ Account with Q Mobile
+ Account with Market Wireless
+ Talk to everyone in your Circles!
+ Chat with Chat Network users
+ CYFYNGEDIG
+ Defnyddio\'r deialydd personol i wneud yr alwad
+ Galwad %1$s oddi wrth %2$s
+ Galwad fideo %1$s oddi wrth %2$s
+ Bydd ateb yn dod a dy alwad %1$s i ben.
+ Bydd ateb yn dod a dy alwadau %1$s i ben.
+ Bydd ateb yn dod a dy alwad fideo %1$s i ben.
+ Bydd ateb yn dod a dy alwad presennol i ben.
+ Bydd ateb yn dod a dy alwadau presennol i ben.
+ Bydd ateb yn dod a dy alwad fideo presennol i ben.
+ Ateb
+ Gwrthod
+ Ni ellir gwneud y galwad am nad oes unrhyw gyfrifon galw sy\u2019n cefnogi\u2019r math hwn o alwad.
+ Ni ellir wneud yr alwad oherwydd dy alwad %1$s.
+ Ni ellir wneud yr alwad oherwydd dy alwadau %1$s.
+ Ni ellir gwneud galwad tra bod galwad ar y gweill mewn ap arall.
+ Galwadau i mewn
+ Galwadau wedi eu methu
+ Rhwystro Galwadau
+ Galwadau yn y cefndir
+ Galwadau wedi\'u datgysylltu
+ Apiau ffôn wedi chwalu
+ Bydd gwneud yr alwad hon yn dod a dy alwad %1$s i ben.
+ Dewisa sut i wneud yr alwad hon
+ Ailgyfierio\u2019r alwad gan ddefnyddio %1$s
+ Galw gan ddefnyddio fy rhif ffôn
+ Ni ellir gwneud y galwad gyda %1$s. Rho gynnig ar ap arall sy\u2019n ailgyfeirio galwadau neu cysyllta â\'r datblygwr am gymorth.
+ Rhwystro Galwadau
+ Rhifau dim yng Nghysylltiadau
+ Rhwystro rhifau na sydd yn dy Gysylltiadau
+ Preifat
+ Rhwystro galwyr na sy\'n datgelu eu rhif
+ Ffôn cyhoeddus
+ Rhwystro galwadau o ffonau cyhoeddus
+ Anhysybys
+ Rhwystro galwadau gan bobol anhysbys
+ Rhwystro Galwadau
+ Analluogwyd Rhwystro Galwadau
+ Wedi gwneud galwad brys
+ Mae Rhwystro Galwadau wedi ei analluogi i ganiatáu’r gwasanaethau brys i gysylltu â thi.
+ Dewislen Datblygwr Telathrebu
+ Ni ellir gwneud galwadau tra bo galwad brys ar y gweill.
+
diff --git a/res/values-eo/strings.xml b/res/values-eo/strings.xml
new file mode 100644
index 000000000..bd11e3733
--- /dev/null
+++ b/res/values-eo/strings.xml
@@ -0,0 +1,30 @@
+
+
+
+ Telefono
+ Nekonata
+ Mesaĝo
+ Q Mobile
+ Market Wireless
+ Sonoma Circles Talk Plus
+ Bay Voice Chat Pro
+ Account with Q Mobile
+ Account with Market Wireless
+ Talk to everyone in your Circles!
+ Chat with Chat Network users
+ RESTRICTED
+ Nekonata
+
diff --git a/res/values-gd/strings.xml b/res/values-gd/strings.xml
new file mode 100644
index 000000000..db844a1bf
--- /dev/null
+++ b/res/values-gd/strings.xml
@@ -0,0 +1,134 @@
+
+
+
+ Stiùireadh nan gairmean
+ Fòn
+ Neo-aithnichte
+ Gairm a dh’fhalbh ort
+ Gairm na h-obrach a dh’fhalbh ort
+ Gairmean a dh’fhalbh ort
+ Dh’fhalbh gairmean ort (%s)
+ Dh’fhalbh gairm o %s ort
+ Fònaig air ais
+ Cuir teachdaireachd
+ Chaidh crìoch a chur air a’ ghairm
+ Chaidh crìoch a chur air a’ ghairm gu %s on a chaidh gairm-èiginn a chur.
+ Chaidh crìoch a chur air a’ ghairm agad on a chaidh gairm-èiginn a chur.
+
+ Gairm sa chùlaibh
+ Chuir %s gairm
+ dhan chùlaibh. Dh’fhaoidte gu bheil an aplacaid seo ag inntrigeadh ’s a’ cluich fuaim thar na gairme.
+
+ Chan eil %s a’ freagairt tuilleadh
+
+ Chleachd a’ ghairm agad aplacaid an fhòn a thàining am broinn an uidheim agad
+
+ Chaidh a’ ghairm a mhùchadh.
+ Tha glaodhaire an fhòn an comas.
+ Chan urrainn dhomh bruidhinn riut an-dràsta. Dè tha dol?
+ Cuiridh mi fòn thugad an ceartair.
+ Cuiridh mi fòn thugad an ceann tamaill.
+ Chan urrainn dhomh bruidhinn riut an-dràsta. An cuir thu fòn thugam an ceann tamaill?
+ Grad-fhreagairtean
+ Deasaich na grad-fhreagairtean
+ Grad-fhreagairt
+ Chaidh an teachdaireachd a chur gu %s.
+ Cha deach an teachdaireachd a chur gu %s.
+ Cunntasan ghairmean
+ Chan eil ach gairmean èiginn ceadaichte.
+ Chan urrainn dhan aplacaid seo gairm a-mach a chur gun chead air an fhòn.
+ Airson gairm a chur, cuir a-steach àireamh dhligheach.
+ Cha ghabh a’ ghairm a chur ris aig an àm seo.
+ Tha àireamh a’ phuist-ghutha a dhìth
+ Chan eil àireamh puist-ghutha air a’ chairt SIM.
+ Cuir àireamh ris
+ An dèan thu aplacaid bhunaiteach an fhòn dhe %s?
+ Suidhich mar a’ bhun-roghainn
+ Sguir dheth
+ ’S urrainn dha %s gairmean a chur is gach nì mun dèidhinn a stiùireadh. Cha bu chòir dhut aplacaid a shuidheachadh mar tè bhunaiteach an fhòn ach ma bhios earbsa agad innte.
+ An dèan thu aplacaid bhunaiteach an sgrìnidh ghairmean dhe %s?
+ Chan urrainn dha %s gairmean a sgrìneadh tuilleadh an uairsin.
+ Chì %s fiosrachadh mu ghairmearan nach eil am measg an luchd-aithne agad agus ’s urrainn dha na gairmean sin a bhacadh. Cha bu chòir dhut aplacaid a shuidheachadh mar tè bhunaiteach an sgrìnidh ghairmean ach ma bhios earbsa agad innte.
+ Suidhich mar a’ bhun-roghainn
+ Sguir dheth
+ Àireamhan bacte
+ Chan fhaigh thu gairm no teachdaireachd on a h-àireamhan bacte.
+ Cuir àireamh ris
+ An dì-bhac thu %1$s?
+ Dì-bhac
+ Bac gairmean is teacsaichean o
+ Àireamh fòn
+ Bac
+ Cha sheall no stiùir ach seilbheadair an uidheim na h-àireamhan bacte.
+ Dì-bhac
+ Tha am bacadh dheth rè seal
+ Nuair a chuireas tu gairm no teacsa gu àireamh-èiginn, thèid am bacadh a chur dheth a dhèanamh cinnteach gun ruig na seirbheisean-èiginn thu.
+ Cuir an comas a-rithist an-dràsta
+ Chaidh %1$s a bhacadh
+ Chaidh %1$s a dhì-bhacadh
+ Cha ghabh àireamh-èiginn a bhacadh.
+ Chaidh %1$s a bhacadh cheana.
+ Q Mobile
+ Market Wireless
+ Sonoma Circles Talk Plus
+ Bay Voice Chat Pro
+ Account with Q Mobile
+ Account with Market Wireless
+ Talk to everyone in your Circles!
+ Chat with Chat Network users
+ RESTRICTED
+ A’ cleachdadh an daithealair pearsanta airson na gairm
+ Gairm %1$s o %2$s
+ Gairm video %1$s o %2$s
+ Ma fhreagras tu, thèid crìoch a chur air a’ ghairm %1$s agad
+ Ma fhreagras tu, thèid crìoch a chur air na gairmean %1$s agad
+ Ma fhreagras tu, thèid crìoch a chur air a’ ghairm video %1$s agad
+ Ma fhreagras tu, thèid crìoch a chur air gairm a tha a’ dol air adhart
+ Ma fhreagras tu, thèid crìoch a chur air gairmean a tha a’ dol air adhart
+ Ma fhreagras tu, thèid crìoch a chur air gairm video a tha a’ dol air adhart
+ Freagair
+ Diùlt
+ Chan urrainn dhuinn a’ ghairm a chur o nach eil cunntas gairmeir ann a chuireas taic ris an t-seòrsa seo de ghairm.
+ Cha ghabh a’ ghairm a chur air sàilleibh na gairme %1$s agad.
+ Cha ghabh a’ ghairm a chur air sàilleibh nan gairmean %1$s agad.
+ Cha ghabh a’ ghairm a chur air sàilleibh gairme am broinn aplacaid eile.
+ Gairmean a-steach
+ Gairmean a dh’fhalbh ort
+ Bacadh ghairmean
+ Gairmean sa chùlaibh
+ Gairmean a chaidh crìoch a chur orra
+ Aplacaidean fòn a thuislich
+ Ma chuireas tu a’ ghairm seo, thèid crìoch a chur air a’ ghairm %1$s agad.
+ Tagh mar a thèid a’ ghairm seo a chur
+ Sìn a’ ghairm air adhart le %1$s
+ Dèan gairm leis an àireamh fòn agam
+ Chan urrainn dha %1$s a’ ghairm seo a chur. Feuch aplacaid sìnidh air adhart eile no cuir fios dhan luchd-leasachaidh airson taic.
+ Bacadh ghairmean
+ Àireamhan nach eil ’nan luchd-aithne
+ Bac àireamhan nach deach a chlàradh san luchd-aithne agad
+ Prìobhaideach
+ Bac luchd-gairm nach nochd an àireamh
+ Bothan-fòn
+ Bac gairmean o bhothain-fhòn
+ Neo-aithnichte
+ Bac gairmean o luchd-ghairm gun aithneachadh
+ Bacadh ghairmean
+ Chaidh bacadh ghairmean a chur à comas
+ Chaidh gairm-èiginn a dhèanamh
+ Chaidh bacadh ghairmean a chur à comas ach am faigh an luchd-cobharach grèim ort.
+ Clàr-taice luchd-leasachaidh telecom
+ Chan urrainn dhut gairm a thogail fhad ’s a bhios tu ann an gairm-èiginn.
+
diff --git a/res/values-kab-rDZ/strings.xml b/res/values-kab-rDZ/strings.xml
new file mode 100644
index 000000000..4c953eedf
--- /dev/null
+++ b/res/values-kab-rDZ/strings.xml
@@ -0,0 +1,32 @@
+
+
+
+ Tiliɣri
+ Arussin
+ Asiwel yettwazgalen
+ Izen
+ Rnu uṭṭun
+ Semmet
+ Semmet
+ Eks asewḥel
+ Uṭṭun n tiliγri
+ Sewḥel
+ Eks asewḥel
+ Tiririt
+ Agwi
+ Uslig
+ Arussin
+
diff --git a/res/values-ku/strings.xml b/res/values-ku/strings.xml
new file mode 100644
index 000000000..5c83edcaa
--- /dev/null
+++ b/res/values-ku/strings.xml
@@ -0,0 +1,41 @@
+
+
+
+ مۆبایل
+ نەزانراو
+ پەیوەندیی لەدەستچوو
+ پەیوەندییە لەدەستچووەکان
+ %s پەیوەندیی لەدەستچوو
+ پەیوەندیی لەدەستچوو لەلایەن %s
+ پەیوەندی کردنەوە
+ نامه
+ پهیوهندی کپ کراوه.
+ بەرزەبێژ تواناپێدراوە.
+ ئێستا ناتوانم قسە بکهم.چی بووه؟
+ هەر ئێستا پەیوەندیت بۆ ئەکەمەوە.
+ دواتر پەیوەندیت بۆ ئەکەمەوە.
+ ئێستا ناتوانم قسە بکهم. دواتر پەیوەندیم پێوە بکه؟
+ وەڵامە خێراکان
+ دهستکاریکردنی وهڵامه خێراکان
+ وهڵامە خێراکان
+ نامه نێردرا بۆ %s.
+ ژمارەی دەنگەپۆست نییە
+ هیچ ژمارەیەکی دەنگەپۆستی لەسەر سیمکارت کۆگا نەکراوە.
+ زیاکردنی ژمارە
+ سنووردارە
+ پەیوەندییە لەدەستچووەکان
+ نەزانراو
+
diff --git a/res/values-lb/strings.xml b/res/values-lb/strings.xml
new file mode 100644
index 000000000..b33c3787d
--- /dev/null
+++ b/res/values-lb/strings.xml
@@ -0,0 +1,43 @@
+
+
+
+ Telefon
+ Onbekannt
+ Verpassten Uruff
+ Verpasst Uriff
+ %s verpasst Uriff
+ Verpassten Uruff vum %s
+ Zréckruffen
+ Message schécken
+ Uruff roueg geschalt.
+ Lautsprecher aktivéiert.
+ Kann elo net schwätzen. Ëm wat geet et?
+ Ech ruffe geschwënn zréck.
+ Ech ruffe méi spéit zréck.
+ Kann elo net schwätzen. Telefonéiere mer méi spéit?
+ Séier Äntwerten
+ Séier Äntwerten änneren
+ Séier Äntwert
+ Message geschéckt un %s.
+ Uruff gouf net gesat, gëff eng gëlteg Nummer an.
+ Den Uruff ka momentan net dobäigesat ginn.
+ Mailbox-Nummer feelt
+ Op der SIM-Kaart ass keng Mailbox-Nummer gespäichert.
+ Nummer dobäisetzen
+ AGESCHRÄNKT
+ Verpasst Uriff
+ Onbekannt
+
diff --git a/res/values-ug/strings.xml b/res/values-ug/strings.xml
new file mode 100644
index 000000000..ed66e86a6
--- /dev/null
+++ b/res/values-ug/strings.xml
@@ -0,0 +1,27 @@
+
+
+
+ تېلېفون
+ يوچۇن
+ سۆزلەشمىگەن چاقىرىش
+ سۆزلەشمىگەن چاقىرىشلار
+ سۆزلەشمىگەن چاقىرىش %s
+ %s دىن كەلگەن جاۋابسىز چاقىرىش
+ قايتۇرۇپ ئۇرۇڭ
+ ئۇچۇر
+ سۆزلەشمىگەن چاقىرىشلار
+ يوچۇن
+
diff --git a/src/com/android/server/telecom/CallScreeningServiceHelper.java b/src/com/android/server/telecom/CallScreeningServiceHelper.java
index f02b9240c..5e47c1f94 100644
--- a/src/com/android/server/telecom/CallScreeningServiceHelper.java
+++ b/src/com/android/server/telecom/CallScreeningServiceHelper.java
@@ -152,6 +152,23 @@ public void onServiceDisconnected(ComponentName name) {
"Cancelling outgoing call screen due to service disconnect.");
}
mFuture.complete(null);
+ mContext.unbindService(this);
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ // No locking needed -- CompletableFuture only lets one thread call complete.
+ Log.continueSession(mLoggingSession, "CSSH.oNB");
+ try {
+ if (!mFuture.isDone()) {
+ Log.w(CallScreeningServiceHelper.this,
+ "Cancelling outgoing call screen due to null binding.");
+ }
+ mFuture.complete(null);
+ mContext.unbindService(this);
} finally {
Log.endSession();
}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
old mode 100755
new mode 100644
index 3bb660ea2..9b9740de8
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -1970,6 +1970,16 @@ public void onCallRedirectionComplete(Call call, Uri handle,
boolean endEarly = false;
String disconnectReason = "";
String callRedirectionApp = mRoleManagerAdapter.getDefaultCallRedirectionApp();
+ PhoneAccount phoneAccount = mPhoneAccountRegistrar
+ .getPhoneAccountUnchecked(phoneAccountHandle);
+ if (phoneAccount != null
+ && !phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+ // Check if the phoneAccountHandle belongs to the current user
+ if (phoneAccountHandle != null &&
+ !phoneAccountHandle.getUserHandle().equals(mCurrentUserHandle)) {
+ phoneAccountHandle = null;
+ }
+ }
boolean isPotentialEmergencyNumber;
try {
@@ -2002,9 +2012,9 @@ public void onCallRedirectionComplete(Call call, Uri handle,
endEarly = true;
disconnectReason = "Null handle from Call Redirection Service";
} else if (phoneAccountHandle == null) {
- Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is null");
+ Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is unavailable");
endEarly = true;
- disconnectReason = "Null phoneAccountHandle from Call Redirection Service";
+ disconnectReason = "Unavailable phoneAccountHandle from Call Redirection Service";
} else if (isPotentialEmergencyNumber) {
Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call"
+ " Redirection Service", handle.getSchemeSpecificPart());
@@ -2025,6 +2035,7 @@ public void onCallRedirectionComplete(Call call, Uri handle,
return;
}
+ final PhoneAccountHandle finalPhoneAccountHandle = phoneAccountHandle;
if (uiAction.equals(CallRedirectionProcessor.UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM)) {
Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMATION);
mPendingRedirectedOutgoingCall = call;
@@ -2034,7 +2045,7 @@ public void onCallRedirectionComplete(Call call, Uri handle,
@Override
public void loggedRun() {
Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMED);
- call.setTargetPhoneAccount(phoneAccountHandle);
+ call.setTargetPhoneAccount(finalPhoneAccountHandle);
placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn,
videoState);
}
@@ -2044,7 +2055,7 @@ public void loggedRun() {
new Runnable("CM.oCRC", mLock) {
@Override
public void loggedRun() {
- call.setTargetPhoneAccount(phoneAccountHandle);
+ call.setTargetPhoneAccount(finalPhoneAccountHandle);
placeOutgoingCall(call, handle, null, speakerphoneOn,
videoState);
}
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
old mode 100755
new mode 100644
index 01acdd185..64fdd8833
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -22,6 +22,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -78,10 +79,17 @@ public void handleCreateConnectionComplete(String callId, ConnectionRequest requ
ParcelableConnection connection, Session.Info sessionInfo) {
Log.startSession(sessionInfo, LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE,
mPackageAbbreviation);
+ UserHandle callingUserHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
logIncoming("handleCreateConnectionComplete %s", callId);
+ // Check status hints image for cross user access
+ if (connection.getStatusHints() != null) {
+ Icon icon = connection.getStatusHints().getIcon();
+ connection.getStatusHints().setIcon(StatusHints.
+ validateAccountIconUserBoundary(icon, callingUserHandle));
+ }
ConnectionServiceWrapper.this
.handleCreateConnectionComplete(callId, request, connection);
@@ -476,6 +484,14 @@ public void addConferenceCall(String callId, ParcelableConference parcelableConf
Log.startSession(sessionInfo, LogUtils.Sessions.CSW_ADD_CONFERENCE_CALL,
mPackageAbbreviation);
+ UserHandle callingUserHandle = Binder.getCallingUserHandle();
+ // Check status hints image for cross user access
+ if (parcelableConference.getStatusHints() != null) {
+ Icon icon = parcelableConference.getStatusHints().getIcon();
+ parcelableConference.getStatusHints().setIcon(StatusHints.
+ validateAccountIconUserBoundary(icon, callingUserHandle));
+ }
+
if (parcelableConference.getConnectElapsedTimeMillis() != 0
&& mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
@@ -721,10 +737,17 @@ public void setAudioRoute(String callId, int audioRoute,
public void setStatusHints(String callId, StatusHints statusHints,
Session.Info sessionInfo) {
Log.startSession(sessionInfo, "CSW.sSH", mPackageAbbreviation);
+ UserHandle callingUserHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
logIncoming("setStatusHints %s %s", callId, statusHints);
+ // Check status hints image for cross user access
+ if (statusHints != null) {
+ Icon icon = statusHints.getIcon();
+ statusHints.setIcon(StatusHints.validateAccountIconUserBoundary(
+ icon, callingUserHandle));
+ }
Call call = mCallIdMapper.getCall(callId);
if (call != null) {
call.setStatusHints(statusHints);
@@ -917,6 +940,14 @@ public void addExistingConnection(String callId, ParcelableConnection connection
} else {
connectIdToCheck = callId;
}
+
+ // Check status hints image for cross user access
+ if (connection.getStatusHints() != null) {
+ Icon icon = connection.getStatusHints().getIcon();
+ connection.getStatusHints().setIcon(StatusHints.
+ validateAccountIconUserBoundary(icon, userHandle));
+ }
+
// Check to see if this Connection has already been added.
Call alreadyAddedConnection = mCallsManager
.getAlreadyAddedConnection(connectIdToCheck);
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index 700dac73c..0a8a6c427 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -657,7 +657,7 @@ public void sortSimPhoneAccountsForEmergency(List accounts,
}
// then by hashcode
- return account1.hashCode() - account2.hashCode();
+ return Integer.compare(account1.hashCode(), account2.hashCode());
});
}
diff --git a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
index f0efe92e1..483dbf6b1 100644
--- a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
@@ -315,8 +315,18 @@ public CallDisposition evaluateCall() {
}
private String getNumberFromCallIntent(Intent intent) {
- String number;
- number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext);
+ String number = null;
+
+ Uri uri = intent.getData();
+ if (uri != null) {
+ String scheme = uri.getScheme();
+ if (scheme != null) {
+ if (scheme.equals("tel") || scheme.equals("sip")) {
+ number = uri.getSchemeSpecificPart();
+ }
+ }
+ }
+
if (TextUtils.isEmpty(number)) {
Log.w(this, "Empty number obtained from the call intent.");
return null;
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index ef2840aca..2669a1d1d 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -29,6 +29,7 @@
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.AsyncTask;
import android.os.PersistableBundle;
@@ -49,6 +50,7 @@
import android.text.TextUtils;
import android.util.AtomicFile;
import android.util.Base64;
+import android.util.EventLog;
import android.util.Xml;
// TODO: Needed for move to system service: import com.android.internal.R;
@@ -131,8 +133,14 @@ public void onPhoneAccountChanged(PhoneAccountRegistrar registrar,
}
public static final String FILE_NAME = "phone-account-registrar-state.xml";
+ public static final String ICON_ERROR_MSG =
+ "Icon cannot be written to memory. Try compressing or downsizing";
@VisibleForTesting
public static final int EXPECTED_STATE_VERSION = 9;
+ public static final int MAX_PHONE_ACCOUNT_REGISTRATIONS = 10;
+ public static final int MAX_PHONE_ACCOUNT_EXTRAS_KEY_PAIR_LIMIT = 100;
+ public static final int MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT = 256;
+ public static final int MAX_SCHEMES_PER_ACCOUNT = 10;
/** Keep in sync with the same in SipSettings.java */
private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES";
@@ -749,6 +757,15 @@ public List getPhoneAccountsForPackage(String packageName,
return getPhoneAccountHandles(0, null, packageName, false, userHandle);
}
+
+ /**
+ * includes disabled, includes crossUserAccess
+ */
+ public List getAllPhoneAccountHandlesForPackage(UserHandle userHandle,
+ String packageName) {
+ return getPhoneAccountHandles(0, null, packageName, true /* includeDisabled */, userHandle);
+ }
+
/**
* Determines if a {@link PhoneAccountHandle} is for a self-managed {@link ConnectionService}.
* @param handle The handle.
@@ -764,8 +781,16 @@ public boolean isSelfManagedPhoneAccount(@NonNull PhoneAccountHandle handle) {
return account.isSelfManaged();
}
- // TODO: Should we implement an artificial limit for # of accounts associated with a single
- // ComponentName?
+ /**
+ * Performs checks before calling addOrReplacePhoneAccount(PhoneAccount)
+ *
+ * @param account The {@code PhoneAccount} to add or replace.
+ * @throws SecurityException if package does not have BIND_TELECOM_CONNECTION_SERVICE
+ * permission
+ * @throws IllegalArgumentException if MAX_PHONE_ACCOUNT_REGISTRATIONS are reached
+ * @throws IllegalArgumentException if MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT is reached
+ * @throws IllegalArgumentException if writing the Icon to memory will cause an Exception
+ */
public void registerPhoneAccount(PhoneAccount account) {
// Enforce the requirement that a connection service for a phone account has the correct
// permission.
@@ -776,10 +801,157 @@ public void registerPhoneAccount(PhoneAccount account) {
throw new SecurityException("PhoneAccount connection service requires "
+ "BIND_TELECOM_CONNECTION_SERVICE permission.");
}
-
+ enforceCharacterLimit(account);
+ enforceIconSizeLimit(account);
+ enforceMaxPhoneAccountLimit(account);
addOrReplacePhoneAccount(account);
}
+ /**
+ * Enforce an upper bound on the number of PhoneAccount's a package can register.
+ * Most apps should only require 1-2. * Include disabled accounts.
+ *
+ * @param account to enforce check on
+ * @throws IllegalArgumentException if MAX_PHONE_ACCOUNT_REGISTRATIONS are reached
+ */
+ private void enforceMaxPhoneAccountLimit(@NonNull PhoneAccount account) {
+ final PhoneAccountHandle accountHandle = account.getAccountHandle();
+ final UserHandle user = accountHandle.getUserHandle();
+ final ComponentName componentName = accountHandle.getComponentName();
+
+ if (getPhoneAccountHandles(0, null, componentName.getPackageName(),
+ true /* includeDisabled */, user).size()
+ >= MAX_PHONE_ACCOUNT_REGISTRATIONS) {
+ EventLog.writeEvent(0x534e4554, "259064622", Binder.getCallingUid(),
+ "enforceMaxPhoneAccountLimit");
+ throw new IllegalArgumentException(
+ "Error, cannot register phone account " + account.getAccountHandle()
+ + " because the limit, " + MAX_PHONE_ACCOUNT_REGISTRATIONS
+ + ", has been reached");
+ }
+ }
+ /**
+ * determine if there will be an issue writing the icon to memory
+ *
+ * @param account to enforce check on
+ * @throws IllegalArgumentException if writing the Icon to memory will cause an Exception
+ */
+ @VisibleForTesting
+ public void enforceIconSizeLimit(PhoneAccount account) {
+ if (account.getIcon() == null) {
+ return;
+ }
+ String text = "";
+ // convert the icon into a Base64 String
+ try {
+ text = XmlSerialization.writeIconToBase64String(account.getIcon());
+ } catch (IOException e) {
+ EventLog.writeEvent(0x534e4554, "259064622", Binder.getCallingUid(),
+ "enforceIconSizeLimit");
+ throw new IllegalArgumentException(ICON_ERROR_MSG);
+ }
+ }
+
+ /**
+ * All {@link PhoneAccount} and{@link PhoneAccountHandle} String and Char-Sequence fields
+ * should be restricted to character limit of MAX_PHONE_ACCOUNT_CHAR_LIMIT to prevent exceptions
+ * when writing large character streams to XML-Serializer.
+ *
+ * @param account to enforce character limit checks on
+ * @throws IllegalArgumentException if MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT reached
+ */
+ public void enforceCharacterLimit(PhoneAccount account) {
+ if (account == null) {
+ return;
+ }
+ PhoneAccountHandle handle = account.getAccountHandle();
+
+ String[] fields =
+ {"Package Name", "Class Name", "PhoneAccountHandle Id", "Label", "ShortDescription",
+ "GroupId", "Address", "SubscriptionAddress"};
+ CharSequence[] args = {handle.getComponentName().getPackageName(),
+ handle.getComponentName().getClassName(), handle.getId(), account.getLabel(),
+ account.getShortDescription(), account.getGroupId(),
+ (account.getAddress() != null ? account.getAddress().toString() : ""),
+ (account.getSubscriptionAddress() != null ?
+ account.getSubscriptionAddress().toString() : "")};
+
+ for (int i = 0; i < fields.length; i++) {
+ if (args[i] != null && args[i].length() > MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT) {
+ EventLog.writeEvent(0x534e4554, "259064622", Binder.getCallingUid(),
+ "enforceCharacterLimit");
+ throw new IllegalArgumentException("The PhoneAccount or PhoneAccountHandle"
+ + fields[i] + " field has an invalid character count. PhoneAccount and "
+ + "PhoneAccountHandle String and Char-Sequence fields are limited to "
+ + MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT + " characters.");
+ }
+ }
+
+ // Enforce limits on the URI Schemes provided
+ enforceLimitsOnSchemes(account);
+
+ // Enforce limit on the PhoneAccount#mExtras
+ Bundle extras = account.getExtras();
+ if (extras != null) {
+ if (extras.keySet().size() > MAX_PHONE_ACCOUNT_EXTRAS_KEY_PAIR_LIMIT) {
+ EventLog.writeEvent(0x534e4554, "259064622", Binder.getCallingUid(),
+ "enforceCharacterLimit");
+ throw new IllegalArgumentException("The PhoneAccount#mExtras is limited to " +
+ MAX_PHONE_ACCOUNT_EXTRAS_KEY_PAIR_LIMIT + " (key,value) pairs.");
+ }
+
+ for (String key : extras.keySet()) {
+ Object value = extras.get(key);
+
+ if ((key != null && key.length() > MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT) ||
+ (value instanceof String &&
+ ((String) value).length() > MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT)) {
+ EventLog.writeEvent(0x534e4554, "259064622", Binder.getCallingUid(),
+ "enforceCharacterLimit");
+ throw new IllegalArgumentException("The PhoneAccount#mExtras contains a String"
+ + " key or value that has an invalid character count. PhoneAccount and "
+ + "PhoneAccountHandle String and Char-Sequence fields are limited to "
+ + MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT + " characters.");
+ }
+ }
+ }
+ }
+
+ /**
+ * Enforce a character limit on all PA and PAH string or char-sequence fields.
+ *
+ * @param account to enforce check on
+ * @throws IllegalArgumentException if MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT reached
+ */
+ @VisibleForTesting
+ public void enforceLimitsOnSchemes(@NonNull PhoneAccount account) {
+ List schemes = account.getSupportedUriSchemes();
+
+ if (schemes == null) {
+ return;
+ }
+
+ if (schemes.size() > MAX_SCHEMES_PER_ACCOUNT) {
+ EventLog.writeEvent(0x534e4554, "259064622", Binder.getCallingUid(),
+ "enforceLimitsOnSchemes");
+ throw new IllegalArgumentException(
+ "Error, cannot register phone account " + account.getAccountHandle()
+ + " because the URI scheme limit of "
+ + MAX_SCHEMES_PER_ACCOUNT + " has been reached");
+ }
+
+ for (String scheme : schemes) {
+ if (scheme.length() > MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT) {
+ EventLog.writeEvent(0x534e4554, "259064622", Binder.getCallingUid(),
+ "enforceLimitsOnSchemes");
+ throw new IllegalArgumentException(
+ "Error, cannot register phone account " + account.getAccountHandle()
+ + " because the max scheme limit of "
+ + MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT + " has been reached");
+ }
+ }
+ }
+
/**
* Adds a {@code PhoneAccount}, replacing an existing one if found.
*
@@ -797,6 +969,7 @@ private void addOrReplacePhoneAccount(PhoneAccount account) {
PhoneAccount oldAccount = getPhoneAccountUnchecked(account.getAccountHandle());
if (oldAccount != null) {
+ enforceSelfManagedAccountUnmodified(account, oldAccount);
mState.accounts.remove(oldAccount);
isEnabled = oldAccount.isEnabled();
Log.i(this, "Modify account: %s", getAccountDiffString(account, oldAccount));
@@ -857,6 +1030,19 @@ public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
}
}
+ private void enforceSelfManagedAccountUnmodified(PhoneAccount newAccount,
+ PhoneAccount oldAccount) {
+ if (oldAccount.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED) &&
+ (!newAccount.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED))) {
+ EventLog.writeEvent(0x534e4554, "246930197");
+ Log.w(this, "Self-managed phone account %s replaced by a non self-managed one",
+ newAccount.getAccountHandle());
+ throw new IllegalArgumentException("Error, cannot change a self-managed "
+ + "phone account " + newAccount.getAccountHandle()
+ + " to other kinds of phone account");
+ }
+ }
+
/**
* Un-registers all phone accounts associated with a specified package.
*
@@ -1487,17 +1673,20 @@ protected void writeBundle(String tagName, Bundle values, XmlSerializer serializ
protected void writeIconIfNonNull(String tagName, Icon value, XmlSerializer serializer)
throws IOException {
if (value != null) {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- value.writeToStream(stream);
- byte[] iconByteArray = stream.toByteArray();
- String text = Base64.encodeToString(iconByteArray, 0, iconByteArray.length, 0);
-
+ String text = writeIconToBase64String(value);
serializer.startTag(null, tagName);
serializer.text(text);
serializer.endTag(null, tagName);
}
}
+ public static String writeIconToBase64String(Icon icon) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ icon.writeToStream(stream);
+ byte[] iconByteArray = stream.toByteArray();
+ return Base64.encodeToString(iconByteArray, 0, iconByteArray.length, 0);
+ }
+
protected void writeLong(String tagName, long value, XmlSerializer serializer)
throws IOException {
serializer.startTag(null, tagName);
diff --git a/src/com/android/server/telecom/ServiceBinder.java b/src/com/android/server/telecom/ServiceBinder.java
index aa2e2a247..7274993e7 100644
--- a/src/com/android/server/telecom/ServiceBinder.java
+++ b/src/com/android/server/telecom/ServiceBinder.java
@@ -150,7 +150,6 @@ public void onServiceConnected(ComponentName componentName, IBinder binder) {
Log.i(this, "Service bound %s", componentName);
Log.addEvent(mCall, LogUtils.Events.CS_BOUND, componentName);
- mCall = null;
// Unbind request was queued so unbind immediately.
if (mIsBindingAborted) {
@@ -192,6 +191,30 @@ public void onServiceDisconnected(ComponentName componentName) {
Log.endSession();
}
}
+
+ /**
+ * Handles the case where the {@link ConnectionService} we bound to returned a null binding.
+ * We want to unbind from the service and cleanup and call resources at this time.
+ * @param componentName The component of the {@link ConnectionService}.
+ */
+ @Override
+ public void onNullBinding(ComponentName componentName) {
+ try {
+ Log.startSession("SBC.oNB");
+ synchronized (mLock) {
+ Log.w(this, "Null binding %s", componentName);
+ Log.addEvent(mCall, "NULL_BINDING", componentName);
+ String componentStr = componentName == null ? "null" : componentName.toString();
+ android.util.EventLog.writeEvent(0x534e4554, "211114016", -1, componentStr);
+ logServiceDisconnected("onNullBinding");
+ mContext.unbindService(this);
+ clearAbort();
+ handleFailedConnection();
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
}
private void handleDisconnect() {
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 9ccae9a37..9b609af49 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -36,7 +36,9 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -65,7 +67,9 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
// TODO: Needed for move to system service: import com.android.internal.R;
@@ -187,25 +191,26 @@ public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle
}
@Override
- public List getCallCapablePhoneAccounts(
+ public ParceledListSlice getCallCapablePhoneAccounts(
boolean includeDisabledAccounts, String callingPackage, String callingFeatureId) {
try {
Log.startSession("TSI.gCCPA");
if (includeDisabledAccounts &&
!canReadPrivilegedPhoneState(
callingPackage, "getCallCapablePhoneAccounts")) {
- return Collections.emptyList();
+ return ParceledListSlice.emptyList();
}
if (!canReadPhoneState(callingPackage, callingFeatureId,
"getCallCapablePhoneAccounts")) {
- return Collections.emptyList();
+ return ParceledListSlice.emptyList();
}
synchronized (mLock) {
final UserHandle callingUserHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
- return mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null,
- includeDisabledAccounts, callingUserHandle);
+ return new ParceledListSlice<>(
+ mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null,
+ includeDisabledAccounts, callingUserHandle));
} catch (Exception e) {
Log.e(this, e, "getCallCapablePhoneAccounts");
throw e;
@@ -219,8 +224,8 @@ public List getCallCapablePhoneAccounts(
}
@Override
- public List getSelfManagedPhoneAccounts(String callingPackage,
- String callingFeatureId) {
+ public ParceledListSlice getSelfManagedPhoneAccounts(
+ String callingPackage, String callingFeatureId) {
try {
Log.startSession("TSI.gSMPA");
if (!canReadPhoneState(callingPackage, callingFeatureId,
@@ -231,8 +236,8 @@ public List getSelfManagedPhoneAccounts(String callingPackag
final UserHandle callingUserHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
- return mPhoneAccountRegistrar.getSelfManagedPhoneAccounts(
- callingUserHandle);
+ return new ParceledListSlice<>(mPhoneAccountRegistrar
+ .getSelfManagedPhoneAccounts(callingUserHandle));
} catch (Exception e) {
Log.e(this, e, "getSelfManagedPhoneAccounts");
throw e;
@@ -246,8 +251,8 @@ public List getSelfManagedPhoneAccounts(String callingPackag
}
@Override
- public List getPhoneAccountsSupportingScheme(String uriScheme,
- String callingPackage) {
+ public ParceledListSlice getPhoneAccountsSupportingScheme(
+ String uriScheme, String callingPackage) {
try {
Log.startSession("TSI.gPASS");
try {
@@ -256,15 +261,16 @@ public List getPhoneAccountsSupportingScheme(String uriSchem
} catch (SecurityException e) {
EventLog.writeEvent(0x534e4554, "62347125", Binder.getCallingUid(),
"getPhoneAccountsSupportingScheme: " + callingPackage);
- return Collections.emptyList();
+ return ParceledListSlice.emptyList();
}
synchronized (mLock) {
final UserHandle callingUserHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
- return mPhoneAccountRegistrar.getCallCapablePhoneAccounts(uriScheme, false,
- callingUserHandle);
+ return new ParceledListSlice<>(mPhoneAccountRegistrar
+ .getCallCapablePhoneAccounts(uriScheme, false,
+ callingUserHandle));
} catch (Exception e) {
Log.e(this, e, "getPhoneAccountsSupportingScheme %s", uriScheme);
throw e;
@@ -278,7 +284,8 @@ public List getPhoneAccountsSupportingScheme(String uriSchem
}
@Override
- public List getPhoneAccountsForPackage(String packageName) {
+ public ParceledListSlice getPhoneAccountsForPackage(
+ String packageName) {
//TODO: Deprecate this in S
try {
enforceCallingPackage(packageName);
@@ -301,8 +308,8 @@ public List getPhoneAccountsForPackage(String packageName) {
long token = Binder.clearCallingIdentity();
try {
Log.startSession("TSI.gPAFP");
- return mPhoneAccountRegistrar.getPhoneAccountsForPackage(packageName,
- callingUserHandle);
+ return new ParceledListSlice<>(mPhoneAccountRegistrar
+ .getAllPhoneAccountHandlesForPackage(callingUserHandle, packageName));
} catch (Exception e) {
Log.e(this, e, "getPhoneAccountsForPackage %s", packageName);
throw e;
@@ -353,7 +360,7 @@ public int getAllPhoneAccountsCount() {
synchronized (mLock) {
try {
// This list is pre-filtered for the calling user.
- return getAllPhoneAccounts().size();
+ return getAllPhoneAccounts().getList().size();
} catch (Exception e) {
Log.e(this, e, "getAllPhoneAccountsCount");
throw e;
@@ -366,7 +373,7 @@ public int getAllPhoneAccountsCount() {
}
@Override
- public List getAllPhoneAccounts() {
+ public ParceledListSlice getAllPhoneAccounts() {
synchronized (mLock) {
try {
Log.startSession("TSI.gAPA");
@@ -382,7 +389,8 @@ public List getAllPhoneAccounts() {
final UserHandle callingUserHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
- return mPhoneAccountRegistrar.getAllPhoneAccounts(callingUserHandle);
+ return new ParceledListSlice<>(mPhoneAccountRegistrar
+ .getAllPhoneAccounts(callingUserHandle));
} catch (Exception e) {
Log.e(this, e, "getAllPhoneAccounts");
throw e;
@@ -396,7 +404,7 @@ public List getAllPhoneAccounts() {
}
@Override
- public List getAllPhoneAccountHandles() {
+ public ParceledListSlice getAllPhoneAccountHandles() {
try {
Log.startSession("TSI.gAPAH");
try {
@@ -412,7 +420,8 @@ public List getAllPhoneAccountHandles() {
final UserHandle callingUserHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
- return mPhoneAccountRegistrar.getAllPhoneAccountHandles(callingUserHandle);
+ return new ParceledListSlice<>(mPhoneAccountRegistrar
+ .getAllPhoneAccountHandles(callingUserHandle));
} catch (Exception e) {
Log.e(this, e, "getAllPhoneAccounts");
throw e;
@@ -531,6 +540,9 @@ public void registerPhoneAccount(PhoneAccount account) {
.build();
}
+ // Validate the profile boundary of the given image URI.
+ validateAccountIconUserBoundary(account.getIcon());
+
final long token = Binder.clearCallingIdentity();
try {
mPhoneAccountRegistrar.registerPhoneAccount(account);
@@ -2353,4 +2365,22 @@ private void broadcastCallScreeningAppChangedIntent(String componentName,
mContext.sendBroadcast(intent);
}
}
+
+ private void validateAccountIconUserBoundary(Icon icon) {
+ // Refer to Icon#getUriString for context. The URI string is invalid for icons of
+ // incompatible types.
+ if (icon != null && (icon.getType() == Icon.TYPE_URI
+ || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
+ String encodedUser = icon.getUri().getEncodedUserInfo();
+ // If there is no encoded user, the URI is calling into the calling user space
+ if (encodedUser != null) {
+ int userId = Integer.parseInt(encodedUser);
+ if (userId != UserHandle.getUserId(Binder.getCallingUid())) {
+ // If we are transcending the profile boundary, throw an error.
+ throw new IllegalArgumentException("Attempting to register a phone account with"
+ + " an image icon belonging to another user.");
+ }
+ }
+ }
+ }
}
diff --git a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
index 486cd8d80..bf8c6f986 100644
--- a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
+++ b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
@@ -201,12 +201,14 @@ public void onServiceConnected(ComponentName componentName, IBinder service) {
public void onServiceDisconnected(ComponentName componentName) {
mResultFuture.complete(mPriorStageResult);
Log.i(this, "Service disconnected.");
+ unbindCallScreeningService();
}
@Override
public void onBindingDied(ComponentName name) {
mResultFuture.complete(mPriorStageResult);
Log.i(this, "Binding died.");
+ unbindCallScreeningService();
}
@Override
diff --git a/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java b/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java
index e93ef2243..879b26603 100644
--- a/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java
+++ b/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java
@@ -151,6 +151,33 @@ public void onServiceDisconnected(ComponentName componentName) {
Log.endSession();
}
}
+
+ @Override
+ public void onNullBinding(ComponentName componentName) {
+ // Make sure we unbind the service if onBind returns null
+ Log.startSession("CRSC.oNB");
+ try {
+ synchronized (mTelecomLock) {
+ finishCallRedirection();
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void onBindingDied(ComponentName componentName) {
+ // Make sure we unbind the service if binding died to avoid background stating
+ // activity leaks
+ Log.startSession("CRSC.oBD");
+ try {
+ synchronized (mTelecomLock) {
+ finishCallRedirection();
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
}
private class CallRedirectionAdapter extends ICallRedirectionAdapter.Stub {
diff --git a/src/com/android/server/telecom/settings/EnableAccountPreferenceActivity.java b/src/com/android/server/telecom/settings/EnableAccountPreferenceActivity.java
index 2367825b3..ad7d7b736 100644
--- a/src/com/android/server/telecom/settings/EnableAccountPreferenceActivity.java
+++ b/src/com/android/server/telecom/settings/EnableAccountPreferenceActivity.java
@@ -25,11 +25,15 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.view.MenuItem;
+import android.view.WindowManager;
public class EnableAccountPreferenceActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ getWindow().addSystemFlags(
+ android.view.WindowManager.LayoutParams
+ .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new EnableAccountPreferenceFragment())
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index 6fd53ebba..382e0b710 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -16,8 +16,11 @@
package com.android.server.telecom.tests;
+import static com.android.server.telecom.tests.ConnectionServiceFixture.STATUS_HINTS_EXTRA;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.nullable;
@@ -35,9 +38,13 @@
import android.content.Context;
import android.content.IContentProvider;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Process;
import android.provider.BlockedNumberContract;
import android.telecom.Call;
@@ -49,12 +56,14 @@
import android.telecom.ParcelableCall;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
+import android.telecom.StatusHints;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
import com.android.internal.telecom.IInCallAdapter;
import android.telecom.CallerInfo;
@@ -180,7 +189,7 @@ public void testTelecomManagerAcceptRingingCall() throws Exception {
@Test
public void testTelecomManagerAcceptRingingVideoCall() throws Exception {
IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
- VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+ VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA, null);
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
@@ -209,7 +218,7 @@ public void testTelecomManagerAcceptRingingVideoCall() throws Exception {
@Test
public void testTelecomManagerAcceptRingingVideoCallAsAudio() throws Exception {
IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
- VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+ VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA, null);
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
@@ -237,7 +246,7 @@ public void testTelecomManagerAcceptRingingVideoCallAsAudio() throws Exception {
@Test
public void testTelecomManagerAcceptRingingInvalidVideoState() throws Exception {
IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
- VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+ VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA, null);
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
@@ -661,13 +670,13 @@ public void run() {
@MediumTest
@Test
public void testBasicConferenceCall() throws Exception {
- makeConferenceCall();
+ makeConferenceCall(null, null);
}
@MediumTest
@Test
public void testAddCallToConference1() throws Exception {
- ParcelableCall conferenceCall = makeConferenceCall();
+ ParcelableCall conferenceCall = makeConferenceCall(null, null);
IdPair callId3 = startAndMakeActiveOutgoingCall("650-555-1214",
mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
// testAddCallToConference{1,2} differ in the order of arguments to InCallAdapter#conference
@@ -685,7 +694,7 @@ public void testAddCallToConference1() throws Exception {
@MediumTest
@Test
public void testAddCallToConference2() throws Exception {
- ParcelableCall conferenceCall = makeConferenceCall();
+ ParcelableCall conferenceCall = makeConferenceCall(null, null);
IdPair callId3 = startAndMakeActiveOutgoingCall("650-555-1214",
mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
mInCallServiceFixtureX.getInCallAdapter()
@@ -944,7 +953,7 @@ public void testPullNonPullableExternalCall() throws Exception {
public void testOutgoingCallSelectPhoneAccountVideo() throws Exception {
startOutgoingPhoneCallPendingCreateConnection("650-555-1212",
null, mConnectionServiceFixtureA,
- Process.myUserHandle(), VideoProfile.STATE_BIDIRECTIONAL);
+ Process.myUserHandle(), VideoProfile.STATE_BIDIRECTIONAL, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
assert(call.isVideoCallingSupportedByPhoneAccount());
@@ -967,7 +976,7 @@ public void testOutgoingCallSelectPhoneAccountVideo() throws Exception {
public void testOutgoingCallSelectPhoneAccountNoVideo() throws Exception {
startOutgoingPhoneCallPendingCreateConnection("650-555-1212",
null, mConnectionServiceFixtureA,
- Process.myUserHandle(), VideoProfile.STATE_BIDIRECTIONAL);
+ Process.myUserHandle(), VideoProfile.STATE_BIDIRECTIONAL, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
assert(call.isVideoCallingSupportedByPhoneAccount());
@@ -1176,4 +1185,145 @@ public void testUnmuteDuringEmergencyCall() throws Exception {
assertTrue(muteValues.get(0));
assertFalse(muteValues.get(1));
}
+
+ /**
+ * Verifies that StatusHints image is validated in ConnectionServiceWrapper#addConferenceCall
+ * when the image doesn't belong to the calling user. Simulates a scenario where an app
+ * could manipulate the contents of the bundle and send it via the binder to upload an image
+ * from another user.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ @Test
+ public void testValidateStatusHintsImage_addConferenceCall() throws Exception {
+ Intent callIntent1 = new Intent();
+ // Stub intent for call2
+ Intent callIntent2 = new Intent();
+ Bundle callExtras1 = new Bundle();
+ Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+ // Load StatusHints extra into TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS to be processed
+ // as the call extras. This will be leveraged in ConnectionServiceFixture to set the
+ // StatusHints for the given connection.
+ StatusHints statusHints = new StatusHints(icon);
+ assertNotNull(statusHints.getIcon());
+ callExtras1.putParcelable(STATUS_HINTS_EXTRA, statusHints);
+ callIntent1.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras1);
+
+ // Start conference call to invoke ConnectionServiceWrapper#addConferenceCall.
+ // Note that the calling user would be User 0.
+ ParcelableCall conferenceCall = makeConferenceCall(callIntent1, callIntent2);
+
+ // Ensure that StatusHints was set.
+ assertNotNull(mInCallServiceFixtureX.getCall(mInCallServiceFixtureX.mLatestCallId)
+ .getStatusHints());
+ // Ensure that the StatusHints image icon was disregarded.
+ assertNull(mInCallServiceFixtureX.getCall(mInCallServiceFixtureX.mLatestCallId)
+ .getStatusHints().getIcon());
+ }
+
+ /**
+ * Verifies that StatusHints image is validated in
+ * ConnectionServiceWrapper#handleCreateConnectionComplete when the image doesn't belong to the
+ * calling user. Simulates a scenario where an app could manipulate the contents of the
+ * bundle and send it via the binder to upload an image from another user.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ @Test
+ public void testValidateStatusHintsImage_handleCreateConnectionComplete() throws Exception {
+ Bundle extras = new Bundle();
+ Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+ // Load the bundle with the test extra in order to simulate an app directly invoking the
+ // binder on ConnectionServiceWrapper#handleCreateConnectionComplete.
+ StatusHints statusHints = new StatusHints(icon);
+ assertNotNull(statusHints.getIcon());
+ extras.putParcelable(STATUS_HINTS_EXTRA, statusHints);
+
+ // Start incoming call with StatusHints extras
+ // Note that the calling user in ConnectionServiceWrapper#handleCreateConnectionComplete
+ // would be User 0.
+ IdPair ids = startIncomingPhoneCallWithExtras("650-555-1212",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA, extras);
+
+ // Ensure that StatusHints was set.
+ assertNotNull(mInCallServiceFixtureX.getCall(ids.mCallId).getStatusHints());
+ // Ensure that the StatusHints image icon was disregarded.
+ assertNull(mInCallServiceFixtureX.getCall(ids.mCallId).getStatusHints().getIcon());
+ }
+
+ /**
+ * Verifies that StatusHints image is validated in ConnectionServiceWrapper#setStatusHints
+ * when the image doesn't belong to the calling user. Simulates a scenario where an app
+ * could manipulate the contents of the bundle and send it via the binder to upload an image
+ * from another user.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ @Test
+ public void testValidateStatusHintsImage_setStatusHints() throws Exception {
+ IdPair outgoing = startAndMakeActiveOutgoingCall("650-555-1214",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+
+ // Modify existing connection with StatusHints image exploit
+ Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+ StatusHints statusHints = new StatusHints(icon);
+ assertNotNull(statusHints.getIcon());
+ ConnectionServiceFixture.ConnectionInfo connectionInfo = mConnectionServiceFixtureA
+ .mConnectionById.get(outgoing.mConnectionId);
+ connectionInfo.statusHints = statusHints;
+
+ // Invoke ConnectionServiceWrapper#setStatusHints.
+ // Note that the calling user would be User 0.
+ mConnectionServiceFixtureA.sendSetStatusHints(outgoing.mConnectionId);
+ waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+ TEST_TIMEOUT);
+
+ // Ensure that StatusHints was set.
+ assertNotNull(mInCallServiceFixtureX.getCall(outgoing.mCallId).getStatusHints());
+ // Ensure that the StatusHints image icon was disregarded.
+ assertNull(mInCallServiceFixtureX.getCall(outgoing.mCallId)
+ .getStatusHints().getIcon());
+ }
+
+ /**
+ * Verifies that StatusHints image is validated in
+ * ConnectionServiceWrapper#addExistingConnection when the image doesn't belong to the calling
+ * user. Simulates a scenario where an app could manipulate the contents of the bundle and
+ * send it via the binder to upload an image from another user.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ @Test
+ public void testValidateStatusHintsImage_addExistingConnection() throws Exception {
+ IdPair outgoing = startAndMakeActiveOutgoingCall("650-555-1214",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+ Connection existingConnection = mConnectionServiceFixtureA.mLatestConnection;
+
+ // Modify existing connection with StatusHints image exploit
+ Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+ StatusHints modifiedStatusHints = new StatusHints(icon);
+ assertNotNull(modifiedStatusHints.getIcon());
+ ConnectionServiceFixture.ConnectionInfo connectionInfo = mConnectionServiceFixtureA
+ .mConnectionById.get(outgoing.mConnectionId);
+ connectionInfo.statusHints = modifiedStatusHints;
+
+ // Invoke ConnectionServiceWrapper#addExistingConnection.
+ // Note that the calling user would be User 0.
+ mConnectionServiceFixtureA.sendAddExistingConnection(outgoing.mConnectionId);
+ waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+ TEST_TIMEOUT);
+
+ // Ensure that StatusHints was set. Due to test setup, the ParcelableConnection object that
+ // is passed into sendAddExistingConnection is instantiated on invocation. The call's
+ // StatusHints are not updated at the time of completion, so instead, we can verify that
+ // the ParcelableConnection object was modified.
+ assertNotNull(mConnectionServiceFixtureA.mLatestParcelableConnection.getStatusHints());
+ // Ensure that the StatusHints image icon was disregarded.
+ assertNull(mConnectionServiceFixtureA.mLatestParcelableConnection
+ .getStatusHints().getIcon());
+ }
}
diff --git a/tests/src/com/android/server/telecom/tests/CallExtrasTest.java b/tests/src/com/android/server/telecom/tests/CallExtrasTest.java
index 926d74078..cf44cfeff 100644
--- a/tests/src/com/android/server/telecom/tests/CallExtrasTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallExtrasTest.java
@@ -370,7 +370,7 @@ public void testExtrasBidirectional() throws Exception {
@LargeTest
@Test
public void testConferenceSetExtras() throws Exception {
- ParcelableCall call = makeConferenceCall();
+ ParcelableCall call = makeConferenceCall(null, null);
String conferenceId = call.getId();
Conference conference = mConnectionServiceFixtureA.mLatestConference;
@@ -414,7 +414,7 @@ public void testConferenceSetExtras() throws Exception {
@FlakyTest(bugId = 117751305)
@Test
public void testConferenceExtraOperations() throws Exception {
- ParcelableCall call = makeConferenceCall();
+ ParcelableCall call = makeConferenceCall(null, null);
String conferenceId = call.getId();
Conference conference = mConnectionServiceFixtureA.mLatestConference;
assertNotNull(conference);
@@ -450,7 +450,7 @@ public void testConferenceExtraOperations() throws Exception {
@LargeTest
@Test
public void testConferenceICS() throws Exception {
- ParcelableCall call = makeConferenceCall();
+ ParcelableCall call = makeConferenceCall(null, null);
String conferenceId = call.getId();
Conference conference = mConnectionServiceFixtureA.mLatestConference;
diff --git a/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java
index ff16880cc..ba2a248d2 100644
--- a/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java
@@ -354,4 +354,28 @@ public void testStripPostDialDigits() throws Exception {
assertEquals(REDIRECTED_GATEWAY_NUMBER_WITH_POST_DIAL,
gatewayInfoArgumentCaptor.getValue().getGatewayAddress());
}
+
+ @Test
+ public void testUnbindOnNullBind() throws Exception {
+ startProcessWithNoGateWayInfo();
+ // To make sure tests are not flaky, clean all the previous handler messages
+ waitForHandlerAction(mProcessor.getHandler(), HANDLER_TIMEOUT_DELAY);
+ enableUserDefinedCallRedirectionService();
+ disableCarrierCallRedirectionService();
+
+ mProcessor.performCallRedirection();
+
+ // Capture the binder
+ ArgumentCaptor serviceConnectionCaptor = ArgumentCaptor.forClass(
+ ServiceConnection.class);
+ // Verify binding occurred
+ verify(mContext, times(1)).bindServiceAsUser(any(Intent.class),
+ serviceConnectionCaptor.capture(), anyInt(), eq(UserHandle.CURRENT));
+ // Simulate null return from onBind
+ serviceConnectionCaptor.getValue().onNullBinding(USER_DEFINED_SERVICE_TEST_COMPONENT_NAME);
+
+ // Verify service was unbound
+ verify(mContext, times(1)).
+ unbindService(any(ServiceConnection.class));
+ }
}
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 4dffe596a..d16430298 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -36,6 +36,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -49,6 +50,9 @@
import android.os.UserHandle;
import android.telecom.CallerInfo;
import android.telecom.Connection;
+import android.telecom.Log;
+import android.telecom.DisconnectCause;
+import android.telecom.GatewayInfo;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -122,8 +126,12 @@
@RunWith(JUnit4.class)
public class CallsManagerTest extends TelecomTestCase {
private static final int TEST_TIMEOUT = 5000; // milliseconds
+ private static final int SECONDARY_USER_ID = 12;
private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1");
+ private static final PhoneAccountHandle SIM_1_HANDLE_SECONDARY = new PhoneAccountHandle(
+ ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1",
+ new UserHandle(SECONDARY_USER_ID));
private static final PhoneAccountHandle SIM_2_HANDLE = new PhoneAccountHandle(
ComponentName.unflattenFromString("com.foo/.Blah"), "Sim2");
private static final PhoneAccountHandle CONNECTION_MGR_1_HANDLE = new PhoneAccountHandle(
@@ -1431,6 +1439,23 @@ public void testRequestDisconnect() throws Exception {
eq(CallState.ACTIVE));
}
+ @SmallTest
+ @Test
+ public void testCrossUserCallRedirectionEndEarlyForIncapablePhoneAccount() {
+ when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(eq(SIM_1_HANDLE_SECONDARY)))
+ .thenReturn(SIM_1_ACCOUNT);
+ mCallsManager.onUserSwitch(UserHandle.SYSTEM);
+
+ Call callSpy = addSpyCall(CallState.NEW);
+ mCallsManager.onCallRedirectionComplete(callSpy, TEST_ADDRESS, SIM_1_HANDLE_SECONDARY,
+ new GatewayInfo("foo", TEST_ADDRESS2, TEST_ADDRESS), true /* speakerphoneOn */,
+ VideoProfile.STATE_AUDIO_ONLY, false /* shouldCancelCall */, "" /* uiAction */);
+
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
+ verify(callSpy).disconnect(argumentCaptor.capture());
+ assertTrue(argumentCaptor.getValue().contains("Unavailable phoneAccountHandle"));
+ }
+
private Call addSpyCall() {
return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE);
}
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index 46b1522df..eaba9ca54 100755
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -67,6 +67,7 @@ public class ConnectionServiceFixture implements TestFixture
static int INVALID_VIDEO_STATE = -1;
public CountDownLatch mExtrasLock = new CountDownLatch(1);
static int NOT_SPECIFIED = 0;
+ public static final String STATUS_HINTS_EXTRA = "updateStatusHints";
/**
* Implementation of ConnectionService that performs no-ops for tasks normally meant for
@@ -101,6 +102,11 @@ public Connection onCreateIncomingConnection(
if (mProperties != NOT_SPECIFIED) {
fakeConnection.setConnectionProperties(mProperties);
}
+ // Testing for StatusHints image icon cross user access
+ if (request.getExtras() != null) {
+ fakeConnection.setStatusHints(
+ request.getExtras().getParcelable(STATUS_HINTS_EXTRA));
+ }
return fakeConnection;
}
@@ -117,6 +123,11 @@ public Connection onCreateOutgoingConnection(
if (mProperties != NOT_SPECIFIED) {
fakeConnection.setConnectionProperties(mProperties);
}
+ // Testing for StatusHints image icon cross user access
+ if (request.getExtras() != null) {
+ fakeConnection.setStatusHints(
+ request.getExtras().getParcelable(STATUS_HINTS_EXTRA));
+ }
return fakeConnection;
}
@@ -133,6 +144,12 @@ public void onConference(Connection cxn1, Connection cxn2) {
Conference fakeConference = new FakeConference();
fakeConference.addConnection(cxn1);
fakeConference.addConnection(cxn2);
+ if (cxn1.getStatusHints() != null || cxn2.getStatusHints() != null) {
+ // For testing purposes, pick one of the status hints that isn't null.
+ StatusHints statusHints = cxn1.getStatusHints() != null
+ ? cxn1.getStatusHints() : cxn2.getStatusHints();
+ fakeConference.setStatusHints(statusHints);
+ }
mLatestConference = fakeConference;
addConference(fakeConference);
} else {
@@ -480,6 +497,7 @@ public class ConferenceInfo {
public String mLatestConnectionId;
public Connection mLatestConnection;
+ public ParcelableConnection mLatestParcelableConnection;
public Conference mLatestConference;
public final Set mConnectionServiceAdapters = new HashSet<>();
public final Map mConnectionById = new HashMap<>();
@@ -724,7 +742,7 @@ private ParcelableConference parcelable(ConferenceInfo c) {
}
private ParcelableConnection parcelable(ConnectionInfo c) {
- return new ParcelableConnection(
+ mLatestParcelableConnection = new ParcelableConnection(
c.request.getAccountHandle(),
c.state,
c.capabilities,
@@ -745,5 +763,6 @@ private ParcelableConnection parcelable(ConnectionInfo c) {
c.conferenceableConnectionIds,
c.extras,
c.callerNumberVerificationStatus);
+ return mLatestParcelableConnection;
}
}
diff --git a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
index 9470008a1..0d523bca1 100644
--- a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
+++ b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
@@ -214,6 +214,19 @@ public void testNoNumberSupplied() {
verifyNoCallPlaced();
}
+ @Test
+ public void testNoCallsPlacedWithContentUri() {
+ Uri handle = Uri.parse("content://com.android.contacts/data/1");
+ Intent intent = new Intent(Intent.ACTION_CALL, handle);
+
+ int result = processIntent(intent, true).disconnectCause;
+
+ assertEquals(DisconnectCause.NO_PHONE_NUMBER_SUPPLIED, result);
+ verify(mContext, never()).getContentResolver();
+ verifyNoBroadcastSent();
+ verifyNoCallPlaced();
+ }
+
@SmallTest
@Test
public void testEmergencyCallWithNonDefaultDialer() {
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index 00ef87c3a..7673e8247 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -22,8 +22,18 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
@@ -70,6 +80,8 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -82,6 +94,9 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase {
private static final int MAX_VERSION = Integer.MAX_VALUE;
private static final String FILE_NAME = "phone-account-registrar-test-1223.xml";
private static final String TEST_LABEL = "right";
+ private static final String TEST_ID = "123";
+ private final String PACKAGE_1 = "PACKAGE_1";
+ private final String PACKAGE_2 = "PACKAGE_2";
private PhoneAccountRegistrar mRegistrar;
@Mock private TelecomManager mTelecomManager;
@Mock private DefaultDialerCache mDefaultDialerCache;
@@ -1006,6 +1021,92 @@ public void testSamePhoneAccountHandlePackage() {
assertFalse(PhoneAccountHandle.areFromSamePackage(null, d));
}
+ /**
+ * Ensure an IllegalArgumentException is thrown when adding more than 10 schemes for a single
+ * account
+ */
+ @Test
+ public void testLimitOnSchemeCount() {
+ PhoneAccountHandle handle = makeQuickAccountHandle(TEST_ID);
+ PhoneAccount.Builder builder = new PhoneAccount.Builder(handle, TEST_LABEL);
+ for (int i = 0; i < PhoneAccountRegistrar.MAX_PHONE_ACCOUNT_REGISTRATIONS + 1; i++) {
+ builder.addSupportedUriScheme(Integer.toString(i));
+ }
+ try {
+ mRegistrar.enforceLimitsOnSchemes(builder.build());
+ fail("should have hit exception in enforceLimitOnSchemes");
+ } catch (IllegalArgumentException e) {
+ // pass test
+ }
+ }
+
+ /**
+ * Ensure an IllegalArgumentException is thrown when adding more 256 chars for a single
+ * account
+ */
+ @Test
+ public void testLimitOnSchemeLength() {
+ PhoneAccountHandle handle = makeQuickAccountHandle(TEST_ID);
+ PhoneAccount.Builder builder = new PhoneAccount.Builder(handle, TEST_LABEL);
+ builder.addSupportedUriScheme(generateStringOfLen(257));
+ try {
+ mRegistrar.enforceLimitsOnSchemes(builder.build());
+ fail("should have hit exception in enforceLimitOnSchemes");
+ } catch (IllegalArgumentException e) {
+ // pass test
+ }
+ }
+
+ /**
+ * Ensure an IllegalArgumentException is thrown when adding an address over the limit
+ */
+ @Test
+ public void testLimitOnAddress() {
+ String text = generateStringOfLen(100);
+ PhoneAccountHandle handle = makeQuickAccountHandle(TEST_ID);
+ PhoneAccount.Builder builder = new PhoneAccount.Builder(handle,TEST_LABEL)
+ .setAddress(Uri.fromParts(text, text, text));
+ try {
+ mRegistrar.enforceCharacterLimit(builder.build());
+ fail("failed to throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // pass test
+ }
+ finally {
+ mRegistrar.unregisterPhoneAccount(handle);
+ }
+ }
+
+ /**
+ * Ensure an IllegalArgumentException is thrown when an Icon that throws an IOException is given
+ */
+ @Test
+ public void testLimitOnIcon() throws Exception {
+ Icon mockIcon = mock(Icon.class);
+ // GIVEN
+ PhoneAccount.Builder builder = new PhoneAccount.Builder(
+ makeQuickAccountHandle(TEST_ID), TEST_LABEL).setIcon(mockIcon);
+ try {
+ // WHEN
+ Mockito.doThrow(new IOException())
+ .when(mockIcon).writeToStream(any(OutputStream.class));
+ //THEN
+ mRegistrar.enforceIconSizeLimit(builder.build());
+ fail("failed to throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // pass test
+ assertTrue(e.getMessage().contains(PhoneAccountRegistrar.ICON_ERROR_MSG));
+ }
+ }
+
+ private String generateStringOfLen(int len){
+ StringBuilder sb = new StringBuilder();
+ for(int i=0; i < len; i++){
+ sb.append("a");
+ }
+ return sb.toString();
+ }
+
private static ComponentName makeQuickConnectionServiceComponentName() {
return new ComponentName(
"com.android.server.telecom.tests",
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index 0dfe29a08..664b31efe 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -31,6 +31,7 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -390,9 +391,11 @@ public void testGetCallCapablePhoneAccounts() throws RemoteException {
makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
assertEquals(fullPHList,
- mTSIBinder.getCallCapablePhoneAccounts(true, DEFAULT_DIALER_PACKAGE, null));
+ mTSIBinder.getCallCapablePhoneAccounts(
+ true, DEFAULT_DIALER_PACKAGE, null).getList());
assertEquals(smallPHList,
- mTSIBinder.getCallCapablePhoneAccounts(false, DEFAULT_DIALER_PACKAGE, null));
+ mTSIBinder.getCallCapablePhoneAccounts(
+ false, DEFAULT_DIALER_PACKAGE, null).getList());
}
@SmallTest
@@ -407,7 +410,7 @@ public void testGetCallCapablePhoneAccountsFailure() throws RemoteException {
List result = null;
try {
- result = mTSIBinder.getCallCapablePhoneAccounts(true, "", null);
+ result = mTSIBinder.getCallCapablePhoneAccounts(true, "", null).getList();
} catch (SecurityException e) {
// intended behavior
}
@@ -435,9 +438,11 @@ public void testGetPhoneAccountsSupportingScheme() throws RemoteException {
makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
assertEquals(telPHList,
- mTSIBinder.getPhoneAccountsSupportingScheme("tel", DEFAULT_DIALER_PACKAGE));
+ mTSIBinder.getPhoneAccountsSupportingScheme(
+ "tel", DEFAULT_DIALER_PACKAGE).getList());
assertEquals(sipPHList,
- mTSIBinder.getPhoneAccountsSupportingScheme("sip", DEFAULT_DIALER_PACKAGE));
+ mTSIBinder.getPhoneAccountsSupportingScheme(
+ "sip", DEFAULT_DIALER_PACKAGE).getList());
}
@SmallTest
@@ -448,12 +453,12 @@ public void testGetPhoneAccountsForPackage() throws RemoteException {
add(SIP_PA_HANDLE_17);
}};
when(mFakePhoneAccountRegistrar
- .getPhoneAccountsForPackage(anyString(), any(UserHandle.class)))
+ .getAllPhoneAccountHandlesForPackage(any(UserHandle.class), anyString()))
.thenReturn(phoneAccountHandleList);
makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
assertEquals(phoneAccountHandleList,
mTSIBinder.getPhoneAccountsForPackage(
- TEL_PA_HANDLE_16.getComponentName().getPackageName()));
+ TEL_PA_HANDLE_16.getComponentName().getPackageName()).getList());
}
@SmallTest
@@ -476,7 +481,7 @@ public void testGetAllPhoneAccounts() throws RemoteException {
when(mFakePhoneAccountRegistrar.getAllPhoneAccounts(any(UserHandle.class)))
.thenReturn(phoneAccountList);
- assertEquals(2, mTSIBinder.getAllPhoneAccounts().size());
+ assertEquals(2, mTSIBinder.getAllPhoneAccounts().getList().size());
}
@SmallTest
@@ -587,6 +592,26 @@ private void registerPhoneAccountTestHelper(PhoneAccount testPhoneAccount,
}
}
+ @SmallTest
+ @Test
+ public void testRegisterPhoneAccountImageIconCrossUser() throws RemoteException {
+ String packageNameToUse = "com.android.officialpackage";
+ PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
+ packageNameToUse, "cs"), "test", Binder.getCallingUserHandle());
+ Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+ PhoneAccount phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
+ doReturn(PackageManager.PERMISSION_GRANTED)
+ .when(mContext).checkCallingOrSelfPermission(MODIFY_PHONE_STATE);
+
+ // This should fail; security exception will be thrown.
+ registerPhoneAccountTestHelper(phoneAccount, false);
+
+ icon = Icon.createWithContentUri("content://0@media/external/images/media/");
+ phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
+ // This should succeed.
+ registerPhoneAccountTestHelper(phoneAccount, true);
+ }
+
@SmallTest
@Test
public void testUnregisterPhoneAccount() throws RemoteException {
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 68b10c6f8..94b4860ee 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -415,12 +415,13 @@ public void tearDown() throws Exception {
super.tearDown();
}
- protected ParcelableCall makeConferenceCall() throws Exception {
- IdPair callId1 = startAndMakeActiveOutgoingCall("650-555-1212",
- mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+ protected ParcelableCall makeConferenceCall(
+ Intent callIntentExtras1, Intent callIntentExtras2) throws Exception {
+ IdPair callId1 = startAndMakeActiveOutgoingCallWithExtras("650-555-1212",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA, callIntentExtras1);
- IdPair callId2 = startAndMakeActiveOutgoingCall("650-555-1213",
- mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+ IdPair callId2 = startAndMakeActiveOutgoingCallWithExtras("650-555-1213",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA, callIntentExtras2);
IInCallAdapter inCallAdapter = mInCallServiceFixtureX.getInCallAdapter();
inCallAdapter.conference(callId1.mCallId, callId2.mCallId);
@@ -636,7 +637,7 @@ protected String startOutgoingPhoneCallWithNoPhoneAccount(String number,
startOutgoingPhoneCallWaitForBroadcaster(number, null,
connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY,
- false /*isEmergency*/);
+ false /*isEmergency*/, null);
return mInCallServiceFixtureX.mLatestCallId;
}
@@ -666,17 +667,17 @@ protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneA
throws Exception {
return startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
- initiatingUser, VideoProfile.STATE_AUDIO_ONLY);
+ initiatingUser, VideoProfile.STATE_AUDIO_ONLY, null);
}
protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
- int videoState) throws Exception {
+ int videoState, Intent callIntentExtras) throws Exception {
int startingNumConnections = connectionServiceFixture.mConnectionById.size();
int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
startOutgoingPhoneCallPendingCreateConnection(number, phoneAccountHandle,
- connectionServiceFixture, initiatingUser, videoState);
+ connectionServiceFixture, initiatingUser, videoState, callIntentExtras);
verify(connectionServiceFixture.getTestDouble(), timeout(TEST_TIMEOUT))
.createConnectionComplete(anyString(), any());
@@ -719,7 +720,7 @@ protected IdPair startOutgoingEmergencyCall(String number,
// Call will not use the ordered broadcaster, since it is an Emergency Call
startOutgoingPhoneCallWaitForBroadcaster(number, phoneAccountHandle,
- connectionServiceFixture, initiatingUser, videoState, true /*isEmergency*/);
+ connectionServiceFixture, initiatingUser, videoState, true /*isEmergency*/, null);
return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
phoneAccountHandle, connectionServiceFixture);
@@ -728,7 +729,7 @@ protected IdPair startOutgoingEmergencyCall(String number,
protected void startOutgoingPhoneCallWaitForBroadcaster(String number,
PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
- int videoState, boolean isEmergency) throws Exception {
+ int videoState, boolean isEmergency, Intent actionCallIntent) throws Exception {
reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
mInCallServiceFixtureY.getTestDouble());
@@ -741,7 +742,9 @@ protected void startOutgoingPhoneCallWaitForBroadcaster(String number,
boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
- Intent actionCallIntent = new Intent();
+ if (actionCallIntent == null) {
+ actionCallIntent = new Intent();
+ }
actionCallIntent.setData(Uri.parse("tel:" + number));
actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
if(isEmergency) {
@@ -786,9 +789,10 @@ protected void startOutgoingPhoneCallWaitForBroadcaster(String number,
protected String startOutgoingPhoneCallPendingCreateConnection(String number,
PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
- int videoState) throws Exception {
+ int videoState, Intent callIntentExtras) throws Exception {
startOutgoingPhoneCallWaitForBroadcaster(number,phoneAccountHandle,
- connectionServiceFixture, initiatingUser, videoState, false /*isEmergency*/);
+ connectionServiceFixture, initiatingUser,
+ videoState, false /*isEmergency*/, callIntentExtras);
waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
verifyAndProcessOutgoingCallBroadcast(phoneAccountHandle);
@@ -893,14 +897,24 @@ protected IdPair startIncomingPhoneCall(
PhoneAccountHandle phoneAccountHandle,
final ConnectionServiceFixture connectionServiceFixture) throws Exception {
return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
- connectionServiceFixture);
+ connectionServiceFixture, null);
+ }
+
+ protected IdPair startIncomingPhoneCallWithExtras(
+ String number,
+ PhoneAccountHandle phoneAccountHandle,
+ final ConnectionServiceFixture connectionServiceFixture,
+ Bundle extras) throws Exception {
+ return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
+ connectionServiceFixture, extras);
}
protected IdPair startIncomingPhoneCall(
String number,
PhoneAccountHandle phoneAccountHandle,
int videoState,
- final ConnectionServiceFixture connectionServiceFixture) throws Exception {
+ final ConnectionServiceFixture connectionServiceFixture,
+ Bundle extras) throws Exception {
reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
mInCallServiceFixtureY.getTestDouble());
@@ -917,7 +931,9 @@ protected IdPair startIncomingPhoneCall(
new IncomingCallAddedListener(incomingCallAddedLatch);
mTelecomSystem.getCallsManager().addListener(callAddedListener);
- Bundle extras = new Bundle();
+ if (extras == null) {
+ extras = new Bundle();
+ }
extras.putParcelable(
TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
@@ -1003,7 +1019,16 @@ protected IdPair startAndMakeActiveOutgoingCall(
PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture) throws Exception {
return startAndMakeActiveOutgoingCall(number, phoneAccountHandle, connectionServiceFixture,
- VideoProfile.STATE_AUDIO_ONLY);
+ VideoProfile.STATE_AUDIO_ONLY, null);
+ }
+
+ protected IdPair startAndMakeActiveOutgoingCallWithExtras(
+ String number,
+ PhoneAccountHandle phoneAccountHandle,
+ ConnectionServiceFixture connectionServiceFixture,
+ Intent callIntentExtras) throws Exception {
+ return startAndMakeActiveOutgoingCall(number, phoneAccountHandle, connectionServiceFixture,
+ VideoProfile.STATE_AUDIO_ONLY, callIntentExtras);
}
// A simple outgoing call, verifying that the appropriate connection service is contacted,
@@ -1011,9 +1036,10 @@ protected IdPair startAndMakeActiveOutgoingCall(
protected IdPair startAndMakeActiveOutgoingCall(
String number,
PhoneAccountHandle phoneAccountHandle,
- ConnectionServiceFixture connectionServiceFixture, int videoState) throws Exception {
+ ConnectionServiceFixture connectionServiceFixture, int videoState,
+ Intent callIntentExtras) throws Exception {
IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
- Process.myUserHandle(), videoState);
+ Process.myUserHandle(), videoState, callIntentExtras);
connectionServiceFixture.sendSetDialing(ids.mConnectionId);
if (phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
diff --git a/tests/src/com/android/server/telecom/tests/VideoCallTests.java b/tests/src/com/android/server/telecom/tests/VideoCallTests.java
index 97e71d18b..84beedc0f 100644
--- a/tests/src/com/android/server/telecom/tests/VideoCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/VideoCallTests.java
@@ -105,7 +105,7 @@ public void testAutoSpeakerphoneOutgoingBidirectional() throws Exception {
// Start an incoming video call.
IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
- VideoProfile.STATE_BIDIRECTIONAL);
+ VideoProfile.STATE_BIDIRECTIONAL, null);
verifyAudioRoute(CallAudioState.ROUTE_SPEAKER);
}
@@ -121,7 +121,7 @@ public void testAutoSpeakerphoneOutgoingTransmitOnly() throws Exception {
// Start an incoming video call.
IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
- VideoProfile.STATE_TX_ENABLED);
+ VideoProfile.STATE_TX_ENABLED, null);
verifyAudioRoute(CallAudioState.ROUTE_SPEAKER);
}
@@ -137,7 +137,7 @@ public void testNoAutoSpeakerphoneOnOutgoing() throws Exception {
// Start an incoming video call.
IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
- VideoProfile.STATE_AUDIO_ONLY);
+ VideoProfile.STATE_AUDIO_ONLY, null);
verifyAudioRoute(CallAudioState.ROUTE_EARPIECE);
}
@@ -165,7 +165,7 @@ public void testNoAutoSpeakerphoneOnIncoming() throws Exception {
@Test
public void testIncomingVideoCallMissedCheckVideoHistory() throws Exception {
IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
- VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+ VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
@@ -182,7 +182,7 @@ public void testIncomingVideoCallMissedCheckVideoHistory() throws Exception {
@Test
public void testIncomingVideoCallRejectedCheckVideoHistory() throws Exception {
IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
- VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+ VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
@@ -201,7 +201,7 @@ public void testIncomingVideoCallRejectedCheckVideoHistory() throws Exception {
public void testOutgoingVideoCallCanceledCheckVideoHistory() throws Exception {
IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
mConnectionServiceFixtureA, Process.myUserHandle(),
- VideoProfile.STATE_BIDIRECTIONAL);
+ VideoProfile.STATE_BIDIRECTIONAL, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
@@ -219,7 +219,7 @@ public void testOutgoingVideoCallCanceledCheckVideoHistory() throws Exception {
public void testOutgoingVideoCallRejectedCheckVideoHistory() throws Exception {
IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
mConnectionServiceFixtureA, Process.myUserHandle(),
- VideoProfile.STATE_BIDIRECTIONAL);
+ VideoProfile.STATE_BIDIRECTIONAL, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
@@ -237,7 +237,7 @@ public void testOutgoingVideoCallRejectedCheckVideoHistory() throws Exception {
public void testOutgoingVideoCallAnsweredAsAudio() throws Exception {
IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
mConnectionServiceFixtureA, Process.myUserHandle(),
- VideoProfile.STATE_BIDIRECTIONAL);
+ VideoProfile.STATE_BIDIRECTIONAL, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();