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();