1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony.data; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.ContentResolver; 23 import android.content.ContentValues; 24 import android.database.ContentObserver; 25 import android.database.Cursor; 26 import android.net.NetworkCapabilities; 27 import android.net.NetworkRequest; 28 import android.net.Uri; 29 import android.os.Handler; 30 import android.os.Looper; 31 import android.os.Message; 32 import android.provider.Telephony; 33 import android.telephony.Annotation; 34 import android.telephony.Annotation.NetworkType; 35 import android.telephony.AnomalyReporter; 36 import android.telephony.CarrierConfigManager; 37 import android.telephony.SubscriptionManager; 38 import android.telephony.TelephonyManager; 39 import android.telephony.data.ApnSetting; 40 import android.telephony.data.DataProfile; 41 import android.telephony.data.TrafficDescriptor; 42 import android.text.TextUtils; 43 import android.util.ArraySet; 44 import android.util.IndentingPrintWriter; 45 import android.util.LocalLog; 46 47 import com.android.internal.telephony.Phone; 48 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback; 49 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback; 50 import com.android.telephony.Rlog; 51 52 import java.io.FileDescriptor; 53 import java.io.PrintWriter; 54 import java.util.ArrayList; 55 import java.util.Comparator; 56 import java.util.List; 57 import java.util.Objects; 58 import java.util.Set; 59 import java.util.UUID; 60 import java.util.concurrent.Executor; 61 import java.util.stream.Collectors; 62 63 /** 64 * DataProfileManager manages the all {@link DataProfile}s for the current 65 * subscription. 66 */ 67 public class DataProfileManager extends Handler { 68 private static final boolean VDBG = true; 69 70 /** Event for APN database changed. */ 71 private static final int EVENT_APN_DATABASE_CHANGED = 2; 72 73 /** Event for SIM refresh. */ 74 private static final int EVENT_SIM_REFRESH = 3; 75 76 private final Phone mPhone; 77 private final String mLogTag; 78 private final LocalLog mLocalLog = new LocalLog(128); 79 80 /** 81 * Should only be used by update updateDataProfiles() to indicate whether resend IA to modem 82 * regardless whether IA changed. 83 **/ 84 private final boolean FORCED_UPDATE_IA = true; 85 private final boolean ONLY_UPDATE_IA_IF_CHANGED = false; 86 87 /** Data network controller. */ 88 private final @NonNull DataNetworkController mDataNetworkController; 89 90 /** Data config manager. */ 91 private final @NonNull DataConfigManager mDataConfigManager; 92 93 /** Cellular data service. */ 94 private final @NonNull DataServiceManager mWwanDataServiceManager; 95 96 /** 97 * All data profiles for the current carrier. Note only data profiles loaded from the APN 98 * database will be stored here. The on-demand data profiles (generated dynamically, for 99 * example, enterprise data profiles with differentiator) are not stored here. 100 */ 101 private final @NonNull List<DataProfile> mAllDataProfiles = new ArrayList<>(); 102 103 /** The data profile used for initial attach. */ 104 private @Nullable DataProfile mInitialAttachDataProfile = null; 105 106 /** The preferred data profile used for internet. */ 107 private @Nullable DataProfile mPreferredDataProfile = null; 108 109 /** Preferred data profile set id. */ 110 private int mPreferredDataProfileSetId = Telephony.Carriers.NO_APN_SET_ID; 111 112 /** Data profile manager callbacks. */ 113 private final @NonNull Set<DataProfileManagerCallback> mDataProfileManagerCallbacks = 114 new ArraySet<>(); 115 116 /** 117 * Data profile manager callback. This should be only used by {@link DataNetworkController}. 118 */ 119 public abstract static class DataProfileManagerCallback extends DataCallback { 120 /** 121 * Constructor 122 * 123 * @param executor The executor of the callback. 124 */ DataProfileManagerCallback(@onNull @allbackExecutor Executor executor)125 public DataProfileManagerCallback(@NonNull @CallbackExecutor Executor executor) { 126 super(executor); 127 } 128 129 /** 130 * Called when data profiles changed. 131 */ onDataProfilesChanged()132 public abstract void onDataProfilesChanged(); 133 } 134 135 /** 136 * Constructor 137 * 138 * @param phone The phone instance. 139 * @param dataNetworkController Data network controller. 140 * @param dataServiceManager WWAN data service manager. 141 * @param looper The looper to be used by the handler. Currently the handler thread is the 142 * phone process's main thread. 143 * @param callback Data profile manager callback. 144 */ DataProfileManager(@onNull Phone phone, @NonNull DataNetworkController dataNetworkController, @NonNull DataServiceManager dataServiceManager, @NonNull Looper looper, @NonNull DataProfileManagerCallback callback)145 public DataProfileManager(@NonNull Phone phone, 146 @NonNull DataNetworkController dataNetworkController, 147 @NonNull DataServiceManager dataServiceManager, @NonNull Looper looper, 148 @NonNull DataProfileManagerCallback callback) { 149 super(looper); 150 mPhone = phone; 151 mLogTag = "DPM-" + mPhone.getPhoneId(); 152 mDataNetworkController = dataNetworkController; 153 mWwanDataServiceManager = dataServiceManager; 154 mDataConfigManager = dataNetworkController.getDataConfigManager(); 155 mDataProfileManagerCallbacks.add(callback); 156 registerAllEvents(); 157 } 158 159 /** 160 * Register for all events that data network controller is interested. 161 */ registerAllEvents()162 private void registerAllEvents() { 163 mDataNetworkController.registerDataNetworkControllerCallback( 164 new DataNetworkControllerCallback(this::post) { 165 @Override 166 public void onInternetDataNetworkConnected( 167 @NonNull List<DataProfile> dataProfiles) { 168 DataProfileManager.this.onInternetDataNetworkConnected(dataProfiles); 169 }}); 170 mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) { 171 @Override 172 public void onCarrierConfigChanged() { 173 DataProfileManager.this.onCarrierConfigUpdated(); 174 } 175 }); 176 mPhone.getContext().getContentResolver().registerContentObserver( 177 Telephony.Carriers.CONTENT_URI, true, new ContentObserver(this) { 178 @Override 179 public void onChange(boolean selfChange) { 180 super.onChange(selfChange); 181 sendEmptyMessage(EVENT_APN_DATABASE_CHANGED); 182 } 183 }); 184 mPhone.mCi.registerForIccRefresh(this, EVENT_SIM_REFRESH, null); 185 } 186 187 @Override handleMessage(Message msg)188 public void handleMessage(Message msg) { 189 switch (msg.what) { 190 case EVENT_SIM_REFRESH: 191 log("Update data profiles due to SIM refresh."); 192 updateDataProfiles(FORCED_UPDATE_IA); 193 break; 194 case EVENT_APN_DATABASE_CHANGED: 195 log("Update data profiles due to APN db updated."); 196 updateDataProfiles(ONLY_UPDATE_IA_IF_CHANGED); 197 break; 198 default: 199 loge("Unexpected event " + msg); 200 break; 201 } 202 } 203 204 /** 205 * Called when carrier config was updated. 206 */ onCarrierConfigUpdated()207 private void onCarrierConfigUpdated() { 208 log("Update data profiles due to carrier config updated."); 209 updateDataProfiles(FORCED_UPDATE_IA); 210 211 //TODO: more works needed to be done here. 212 } 213 214 /** 215 * Check if there are any Enterprise APN configured by DPC and return a data profile 216 * with the same. 217 * @return data profile with enterprise ApnSetting if available, else null 218 */ getEnterpriseDataProfile()219 @Nullable private DataProfile getEnterpriseDataProfile() { 220 Cursor cursor = mPhone.getContext().getContentResolver().query( 221 Telephony.Carriers.DPC_URI, null, null, null, null); 222 if (cursor == null) { 223 loge("Cannot access APN database through telephony provider."); 224 return null; 225 } 226 227 DataProfile dataProfile = null; 228 while (cursor.moveToNext()) { 229 ApnSetting apn = ApnSetting.makeApnSetting(cursor); 230 if (apn != null) { 231 dataProfile = new DataProfile.Builder() 232 .setApnSetting(apn) 233 .setTrafficDescriptor(new TrafficDescriptor(apn.getApnName(), null)) 234 .setPreferred(false) 235 .build(); 236 if (dataProfile.canSatisfy(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) { 237 break; 238 } 239 } 240 } 241 cursor.close(); 242 return dataProfile; 243 } 244 /** 245 * Update all data profiles, including preferred data profile, and initial attach data profile. 246 * Also send those profiles down to the modem if needed. 247 * 248 * @param forceUpdateIa If {@code true}, we should always send IA again to modem. 249 */ updateDataProfiles(boolean forceUpdateIa)250 private void updateDataProfiles(boolean forceUpdateIa) { 251 List<DataProfile> profiles = new ArrayList<>(); 252 if (mDataConfigManager.isConfigCarrierSpecific()) { 253 Cursor cursor = mPhone.getContext().getContentResolver().query( 254 Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, "filtered/subId/" 255 + mPhone.getSubId()), null, null, null, Telephony.Carriers._ID); 256 if (cursor == null) { 257 loge("Cannot access APN database through telephony provider."); 258 return; 259 } 260 boolean isInternetSupported = false; 261 while (cursor.moveToNext()) { 262 ApnSetting apn = ApnSetting.makeApnSetting(cursor); 263 if (apn != null) { 264 DataProfile dataProfile = new DataProfile.Builder() 265 .setApnSetting(apn) 266 .setTrafficDescriptor(new TrafficDescriptor(apn.getApnName(), null)) 267 .setPreferred(false) 268 .build(); 269 profiles.add(dataProfile); 270 log("Added " + dataProfile); 271 272 isInternetSupported |= apn.canHandleType(ApnSetting.TYPE_DEFAULT); 273 if (mDataConfigManager.isApnConfigAnomalyReportEnabled()) { 274 checkApnSetting(apn); 275 } 276 } 277 } 278 cursor.close(); 279 280 if (!isInternetSupported 281 && !profiles.isEmpty() // APN database has been read successfully 282 && mDataConfigManager.isApnConfigAnomalyReportEnabled()) { 283 reportAnomaly("Carrier doesn't support internet.", 284 "9af73e18-b523-4dc5-adab-363eb6613305"); 285 } 286 } 287 288 // Check if any of the profile already supports ENTERPRISE, if not, check if DPC has 289 // configured one and retrieve the same. 290 DataProfile dataProfile = profiles.stream() 291 .filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) 292 .findFirst() 293 .orElse(null); 294 if (dataProfile == null) { 295 dataProfile = getEnterpriseDataProfile(); 296 if (dataProfile != null) { 297 profiles.add(dataProfile); 298 log("Added enterprise profile " + dataProfile); 299 } 300 } 301 302 // Check if any of the profile already supports IMS, if not, add the default one. 303 dataProfile = profiles.stream() 304 .filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_IMS)) 305 .findFirst() 306 .orElse(null); 307 if (dataProfile == null) { 308 profiles.add(new DataProfile.Builder() 309 .setApnSetting(buildDefaultApnSetting("DEFAULT IMS", "ims", 310 ApnSetting.TYPE_IMS)) 311 .setTrafficDescriptor(new TrafficDescriptor("ims", null)) 312 .build()); 313 log("Added default IMS data profile."); 314 } 315 316 // Check if any of the profile already supports EIMS, if not, add the default one. 317 dataProfile = profiles.stream() 318 .filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_EIMS)) 319 .findFirst() 320 .orElse(null); 321 if (dataProfile == null) { 322 profiles.add(new DataProfile.Builder() 323 .setApnSetting(buildDefaultApnSetting("DEFAULT EIMS", "sos", 324 ApnSetting.TYPE_EMERGENCY)) 325 .setTrafficDescriptor(new TrafficDescriptor("sos", null)) 326 .build()); 327 log("Added default EIMS data profile."); 328 } 329 330 dedupeDataProfiles(profiles); 331 332 if (mDataConfigManager.isApnConfigAnomalyReportEnabled()) { 333 checkDataProfiles(profiles); 334 } 335 336 log("Found " + profiles.size() + " data profiles. profiles = " + profiles); 337 338 boolean profilesChanged = false; 339 if (mAllDataProfiles.size() != profiles.size() || !mAllDataProfiles.containsAll(profiles)) { 340 log("Data profiles changed."); 341 mAllDataProfiles.clear(); 342 mAllDataProfiles.addAll(profiles); 343 profilesChanged = true; 344 } 345 346 // Reload the latest preferred data profile from either database or config. 347 profilesChanged |= updatePreferredDataProfile(); 348 349 int setId = getPreferredDataProfileSetId(); 350 if (setId != mPreferredDataProfileSetId) { 351 logl("Changed preferred data profile set id to " + setId); 352 mPreferredDataProfileSetId = setId; 353 profilesChanged = true; 354 } 355 356 updateDataProfilesAtModem(); 357 updateInitialAttachDataProfileAtModem(forceUpdateIa); 358 359 if (profilesChanged) { 360 mDataProfileManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( 361 callback::onDataProfilesChanged)); 362 } 363 } 364 365 /** 366 * @return The preferred data profile set id. 367 */ getPreferredDataProfileSetId()368 private int getPreferredDataProfileSetId() { 369 // Query the preferred APN set id. The set id is automatically set when we set by 370 // TelephonyProvider when setting preferred APN in setPreferredDataProfile(). 371 Cursor cursor = mPhone.getContext().getContentResolver() 372 .query(Uri.withAppendedPath(Telephony.Carriers.PREFERRED_APN_SET_URI, 373 String.valueOf(mPhone.getSubId())), 374 new String[] {Telephony.Carriers.APN_SET_ID}, null, null, null); 375 // Returns all APNs for the current carrier which have an apn_set_id 376 // equal to the preferred APN (if no preferred APN, or if the preferred APN has no set id, 377 // the query will return null) 378 if (cursor == null) { 379 log("getPreferredDataProfileSetId: cursor is null"); 380 return Telephony.Carriers.NO_APN_SET_ID; 381 } 382 383 int setId; 384 if (cursor.getCount() < 1) { 385 loge("getPreferredDataProfileSetId: no APNs found"); 386 setId = Telephony.Carriers.NO_APN_SET_ID; 387 } else { 388 cursor.moveToFirst(); 389 setId = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN_SET_ID)); 390 } 391 392 cursor.close(); 393 return setId; 394 } 395 396 /** 397 * Called when internet data is connected. 398 * 399 * @param dataProfiles The connected internet data networks' profiles. 400 */ onInternetDataNetworkConnected(@onNull List<DataProfile> dataProfiles)401 private void onInternetDataNetworkConnected(@NonNull List<DataProfile> dataProfiles) { 402 // If there is already a preferred data profile set, then we don't need to do anything. 403 if (mPreferredDataProfile != null) return; 404 405 // If there is no preferred data profile, then we should use one of the data profiles, 406 // which is good for internet, as the preferred data profile. 407 408 // Most of the cases there should be only one, but in case there are multiple, choose the 409 // one which has longest life cycle. 410 DataProfile dataProfile = dataProfiles.stream() 411 .max(Comparator.comparingLong(DataProfile::getLastSetupTimestamp).reversed()) 412 .orElse(null); 413 // Save the preferred data profile into database. 414 setPreferredDataProfile(dataProfile); 415 updateDataProfiles(ONLY_UPDATE_IA_IF_CHANGED); 416 } 417 418 /** 419 * Get the preferred data profile for internet data. 420 * 421 * @return The preferred data profile. 422 */ getPreferredDataProfileFromDb()423 private @Nullable DataProfile getPreferredDataProfileFromDb() { 424 Cursor cursor = mPhone.getContext().getContentResolver().query( 425 Uri.withAppendedPath(Telephony.Carriers.PREFERRED_APN_URI, 426 String.valueOf(mPhone.getSubId())), null, null, null, 427 Telephony.Carriers.DEFAULT_SORT_ORDER); 428 DataProfile dataProfile = null; 429 if (cursor != null) { 430 if (cursor.getCount() > 0) { 431 cursor.moveToFirst(); 432 int apnId = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)); 433 dataProfile = mAllDataProfiles.stream() 434 .filter(dp -> dp.getApnSetting() != null 435 && dp.getApnSetting().getId() == apnId) 436 .findFirst() 437 .orElse(null); 438 } 439 cursor.close(); 440 } 441 log("getPreferredDataProfileFromDb: " + dataProfile); 442 return dataProfile; 443 } 444 445 /** 446 * @return The preferred data profile from carrier config. 447 */ getPreferredDataProfileFromConfig()448 private @Nullable DataProfile getPreferredDataProfileFromConfig() { 449 // Check if there is configured default preferred data profile. 450 String defaultPreferredApn = mDataConfigManager.getDefaultPreferredApn(); 451 if (!TextUtils.isEmpty(defaultPreferredApn)) { 452 return mAllDataProfiles.stream() 453 .filter(dp -> dp.getApnSetting() != null && defaultPreferredApn.equals( 454 dp.getApnSetting().getApnName())) 455 .findFirst() 456 .orElse(null); 457 } 458 return null; 459 } 460 461 /** 462 * Save the preferred data profile into the database. 463 * 464 * @param dataProfile The preferred data profile used for internet data. {@code null} to clear 465 * the preferred data profile from database. 466 */ setPreferredDataProfile(@ullable DataProfile dataProfile)467 private void setPreferredDataProfile(@Nullable DataProfile dataProfile) { 468 log("setPreferredDataProfile: " + dataProfile); 469 470 String subId = Long.toString(mPhone.getSubId()); 471 Uri uri = Uri.withAppendedPath(Telephony.Carriers.PREFERRED_APN_URI, subId); 472 ContentResolver resolver = mPhone.getContext().getContentResolver(); 473 resolver.delete(uri, null, null); 474 475 if (dataProfile != null && dataProfile.getApnSetting() != null) { 476 ContentValues values = new ContentValues(); 477 // Fill only the id here. TelephonyProvider will pull the rest of key fields and write 478 // into the database. 479 values.put(Telephony.Carriers.APN_ID, dataProfile.getApnSetting().getId()); 480 resolver.insert(uri, values); 481 } 482 } 483 484 /** 485 * Reload the latest preferred data profile from either database or the config. This is to 486 * make sure the cached {@link #mPreferredDataProfile} is in-sync. 487 * 488 * @return {@code true} if preferred data profile changed. 489 */ updatePreferredDataProfile()490 private boolean updatePreferredDataProfile() { 491 DataProfile preferredDataProfile; 492 if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) { 493 preferredDataProfile = getPreferredDataProfileFromDb(); 494 if (preferredDataProfile == null) { 495 preferredDataProfile = getPreferredDataProfileFromConfig(); 496 if (preferredDataProfile != null) { 497 // Save the carrier specified preferred data profile into database 498 setPreferredDataProfile(preferredDataProfile); 499 } else { 500 preferredDataProfile = mAllDataProfiles.stream() 501 .filter(dp -> areDataProfilesSharingApn(dp, mPreferredDataProfile)) 502 .findFirst() 503 .orElse(null); 504 if (preferredDataProfile != null) { 505 log("updatePreferredDataProfile: preferredDB is empty and no carrier " 506 + "default configured, setting preferred to be prev preferred DP."); 507 setPreferredDataProfile(preferredDataProfile); 508 } 509 } 510 } 511 } else { 512 preferredDataProfile = null; 513 } 514 515 for (DataProfile dataProfile : mAllDataProfiles) { 516 dataProfile.setPreferred(dataProfile.equals(preferredDataProfile)); 517 } 518 519 if (!Objects.equals(mPreferredDataProfile, preferredDataProfile)) { 520 mPreferredDataProfile = preferredDataProfile; 521 522 logl("Changed preferred data profile to " + mPreferredDataProfile); 523 return true; 524 } 525 return false; 526 } 527 528 /** 529 * Update the data profile used for initial attach. 530 * 531 * Note that starting from Android 13 only APNs that supports "IA" type will be used for 532 * initial attach. Please update APN configuration file if needed. 533 * 534 * Some carriers might explicitly require that using "user-added" APN for initial 535 * attach. In this case, exception can be configured through 536 * {@link CarrierConfigManager#KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY}. 537 * 538 * @param forceUpdateIa If {@code true}, we should always send IA again to modem. 539 */ updateInitialAttachDataProfileAtModem(boolean forceUpdateIa)540 private void updateInitialAttachDataProfileAtModem(boolean forceUpdateIa) { 541 DataProfile initialAttachDataProfile = null; 542 543 // Sort the data profiles so the preferred data profile is at the beginning. 544 List<DataProfile> allDataProfiles = mAllDataProfiles.stream() 545 .sorted(Comparator.comparing((DataProfile dp) -> !dp.equals(mPreferredDataProfile))) 546 .collect(Collectors.toList()); 547 // Search in the order. "IA" type should be the first from getAllowedInitialAttachApnTypes. 548 for (int apnType : mDataConfigManager.getAllowedInitialAttachApnTypes()) { 549 initialAttachDataProfile = allDataProfiles.stream() 550 .filter(dp -> dp.canSatisfy(DataUtils.apnTypeToNetworkCapability(apnType))) 551 .findFirst() 552 .orElse(null); 553 if (initialAttachDataProfile != null) break; 554 } 555 556 if (forceUpdateIa || !Objects.equals(mInitialAttachDataProfile, initialAttachDataProfile)) { 557 mInitialAttachDataProfile = initialAttachDataProfile; 558 logl("Initial attach data profile updated as " + mInitialAttachDataProfile 559 + " or forceUpdateIa= " + forceUpdateIa); 560 // TODO: Push the null data profile to modem on new AIDL HAL. Modem should clear the IA 561 // APN, tracking for U b/227579876, now using forceUpdateIa which always push to modem 562 if (mInitialAttachDataProfile != null) { 563 mWwanDataServiceManager.setInitialAttachApn(mInitialAttachDataProfile, 564 mPhone.getServiceState().getDataRoamingFromRegistration(), null); 565 } 566 } 567 } 568 569 /** 570 * Update the data profiles at modem. 571 */ updateDataProfilesAtModem()572 private void updateDataProfilesAtModem() { 573 log("updateDataProfilesAtModem: set " + mAllDataProfiles.size() + " data profiles."); 574 mWwanDataServiceManager.setDataProfile(mAllDataProfiles, 575 mPhone.getServiceState().getDataRoamingFromRegistration(), null); 576 } 577 578 /** 579 * Create default apn settings for the apn type like emergency, and ims 580 * 581 * @param entry Entry name 582 * @param apn APN name 583 * @param apnTypeBitmask APN type 584 * @return The APN setting 585 */ buildDefaultApnSetting(@onNull String entry, @NonNull String apn, @Annotation.ApnType int apnTypeBitmask)586 private @NonNull ApnSetting buildDefaultApnSetting(@NonNull String entry, 587 @NonNull String apn, @Annotation.ApnType int apnTypeBitmask) { 588 return new ApnSetting.Builder() 589 .setEntryName(entry) 590 .setProtocol(ApnSetting.PROTOCOL_IPV4V6) 591 .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6) 592 .setApnName(apn) 593 .setApnTypeBitmask(apnTypeBitmask) 594 .setCarrierEnabled(true) 595 .setApnSetId(Telephony.Carriers.MATCH_ALL_APN_SET_ID) 596 .build(); 597 } 598 599 /** 600 * Get the data profile that can satisfy the network request. 601 * 602 * @param networkRequest The network request. 603 * @param networkType The current data network type. 604 * @param ignorePermanentFailure {@code true} to ignore {@link ApnSetting#getPermanentFailed()}. 605 * This should be set to true for condition-based retry/setup. 606 * @return The data profile. {@code null} if can't find any satisfiable data profile. 607 */ getDataProfileForNetworkRequest( @onNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType, boolean ignorePermanentFailure)608 public @Nullable DataProfile getDataProfileForNetworkRequest( 609 @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType, 610 boolean ignorePermanentFailure) { 611 ApnSetting apnSetting = null; 612 if (networkRequest.hasAttribute(TelephonyNetworkRequest 613 .CAPABILITY_ATTRIBUTE_APN_SETTING)) { 614 apnSetting = getApnSettingForNetworkRequest(networkRequest, networkType, 615 ignorePermanentFailure); 616 } 617 618 TrafficDescriptor.Builder trafficDescriptorBuilder = new TrafficDescriptor.Builder(); 619 if (networkRequest.hasAttribute(TelephonyNetworkRequest 620 .CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN)) { 621 if (apnSetting != null) { 622 trafficDescriptorBuilder.setDataNetworkName(apnSetting.getApnName()); 623 } 624 } 625 626 if (networkRequest.hasAttribute( 627 TelephonyNetworkRequest.CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID)) { 628 TrafficDescriptor.OsAppId osAppId = networkRequest.getOsAppId(); 629 if (osAppId != null) { 630 trafficDescriptorBuilder.setOsAppId(osAppId.getBytes()); 631 } 632 } 633 634 TrafficDescriptor trafficDescriptor; 635 try { 636 trafficDescriptor = trafficDescriptorBuilder.build(); 637 } catch (IllegalArgumentException e) { 638 // We reach here when both ApnSetting and trafficDescriptor are null. 639 log("Unable to find a data profile for " + networkRequest); 640 return null; 641 } 642 643 // Instead of building the data profile from APN setting and traffic descriptor on-the-fly, 644 // find the existing one from mAllDataProfiles so the last-setup timestamp can be retained. 645 // Only create a new one when it can't be found. 646 for (DataProfile dataProfile : mAllDataProfiles) { 647 if (Objects.equals(apnSetting, dataProfile.getApnSetting()) 648 && trafficDescriptor.equals(dataProfile.getTrafficDescriptor())) { 649 return dataProfile; 650 } 651 } 652 653 // When reaching here, it means that we have a valid non-null traffic descriptor, but 654 // could not find it in mAllDataProfiles. This could happen on the traffic descriptor 655 // capable capabilities like ENTERPRISE. 656 DataProfile.Builder profileBuilder = new DataProfile.Builder(); 657 if (apnSetting != null) { 658 profileBuilder.setApnSetting(apnSetting); 659 } 660 661 // trafficDescriptor is always non-null when we reach here. 662 profileBuilder.setTrafficDescriptor(trafficDescriptor); 663 664 DataProfile dataProfile = profileBuilder.build(); 665 log("Created data profile " + dataProfile + " for " + networkRequest); 666 return dataProfile; 667 } 668 669 /** 670 * Get the APN setting for the network request. 671 * 672 * @param networkRequest The network request. 673 * @param networkType The current data network type. 674 * @param ignorePermanentFailure {@code true} to ignore {@link ApnSetting#getPermanentFailed()}. 675 * This should be set to true for condition-based retry/setup. 676 * @return The APN setting. {@code null} if can't find any satisfiable data profile. 677 */ getApnSettingForNetworkRequest( @onNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType, boolean ignorePermanentFailure)678 private @Nullable ApnSetting getApnSettingForNetworkRequest( 679 @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType, 680 boolean ignorePermanentFailure) { 681 if (!networkRequest.hasAttribute( 682 TelephonyNetworkRequest.CAPABILITY_ATTRIBUTE_APN_SETTING)) { 683 loge("Network request does not have APN setting attribute."); 684 return null; 685 } 686 687 // If the preferred data profile can be used, always use it if it can satisfy the network 688 // request with current network type (even though it's been marked as permanent failed.) 689 if (mPreferredDataProfile != null 690 && networkRequest.canBeSatisfiedBy(mPreferredDataProfile) 691 && mPreferredDataProfile.getApnSetting() != null 692 && mPreferredDataProfile.getApnSetting().canSupportNetworkType(networkType)) { 693 if (ignorePermanentFailure || !mPreferredDataProfile.getApnSetting() 694 .getPermanentFailed()) { 695 return mPreferredDataProfile.getApnSetting(); 696 } 697 log("The preferred data profile is permanently failed. Only condition based retry " 698 + "can happen."); 699 return null; 700 } 701 702 // Filter out the data profile that can't satisfy the request. 703 // Preferred data profile should be returned in the top of the list. 704 List<DataProfile> dataProfiles = mAllDataProfiles.stream() 705 .filter(networkRequest::canBeSatisfiedBy) 706 // The longest time hasn't used data profile will be in the front so all the data 707 // profiles can be tried. 708 .sorted(Comparator.comparing(DataProfile::getLastSetupTimestamp)) 709 .collect(Collectors.toList()); 710 for (DataProfile dataProfile : dataProfiles) { 711 logv("Satisfied profile: " + dataProfile + ", last setup=" 712 + DataUtils.elapsedTimeToString(dataProfile.getLastSetupTimestamp())); 713 } 714 if (dataProfiles.size() == 0) { 715 log("Can't find any data profile that can satisfy " + networkRequest); 716 return null; 717 } 718 719 // Check if the remaining data profiles can used in current data network type. 720 dataProfiles = dataProfiles.stream() 721 .filter(dp -> dp.getApnSetting() != null 722 && dp.getApnSetting().canSupportNetworkType(networkType)) 723 .collect(Collectors.toList()); 724 if (dataProfiles.size() == 0) { 725 log("Can't find any data profile for network type " 726 + TelephonyManager.getNetworkTypeName(networkType)); 727 return null; 728 } 729 730 // Check if preferred data profile set id matches. 731 dataProfiles = dataProfiles.stream() 732 .filter(dp -> dp.getApnSetting() != null 733 && (dp.getApnSetting().getApnSetId() 734 == Telephony.Carriers.MATCH_ALL_APN_SET_ID 735 || dp.getApnSetting().getApnSetId() == mPreferredDataProfileSetId)) 736 .collect(Collectors.toList()); 737 if (dataProfiles.size() == 0) { 738 log("Can't find any data profile has APN set id matched. mPreferredDataProfileSetId=" 739 + mPreferredDataProfileSetId); 740 return null; 741 } 742 743 // Check if data profiles are permanently failed. 744 dataProfiles = dataProfiles.stream() 745 .filter(dp -> ignorePermanentFailure || !dp.getApnSetting().getPermanentFailed()) 746 .collect(Collectors.toList()); 747 if (dataProfiles.size() == 0) { 748 log("The suitable data profiles are all in permanent failed state."); 749 return null; 750 } 751 752 return dataProfiles.get(0).getApnSetting(); 753 } 754 755 /** 756 * Check if the data profile is essentially the preferred data profile. The non-essential 757 * elements include e.g.APN Id. 758 * 759 * @param dataProfile The data profile to check. 760 * @return {@code true} if the data profile is essentially the preferred data profile. 761 */ isDataProfilePreferred(@onNull DataProfile dataProfile)762 public boolean isDataProfilePreferred(@NonNull DataProfile dataProfile) { 763 return areDataProfilesSharingApn(dataProfile, mPreferredDataProfile); 764 } 765 766 /** 767 * Check if there is tethering data profile for certain network type. 768 * 769 * @param networkType The network type 770 * @return {@code true} if tethering data profile is found. {@code false} if no specific profile 771 * should used for tethering. In this case, tethering service will use internet network for 772 * tethering. 773 */ isTetheringDataProfileExisting(@etworkType int networkType)774 public boolean isTetheringDataProfileExisting(@NetworkType int networkType) { 775 if (mDataConfigManager.isTetheringProfileDisabledForRoaming() 776 && mPhone.getServiceState().getDataRoaming()) { 777 // Use internet network for tethering. 778 return false; 779 } 780 TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest( 781 new NetworkRequest.Builder() 782 .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) 783 .build(), mPhone); 784 return getDataProfileForNetworkRequest(networkRequest, networkType, true) != null; 785 } 786 787 /** 788 * Check if any preferred data profile exists. 789 * 790 * @return {@code true} if any preferred data profile exists 791 */ isAnyPreferredDataProfileExisting()792 public boolean isAnyPreferredDataProfileExisting() { 793 for (DataProfile dataProfile : mAllDataProfiles) { 794 if (dataProfile.isPreferred()) return true; 795 } 796 return false; 797 } 798 799 /** 800 * Dedupe the similar data profiles. 801 */ dedupeDataProfiles(@onNull List<DataProfile> dataProfiles)802 private void dedupeDataProfiles(@NonNull List<DataProfile> dataProfiles) { 803 int i = 0; 804 while (i < dataProfiles.size() - 1) { 805 DataProfile first = dataProfiles.get(i); 806 int j = i + 1; 807 while (j < dataProfiles.size()) { 808 DataProfile second = dataProfiles.get(j); 809 DataProfile merged = mergeDataProfiles(first, second); 810 if (merged != null) { 811 log("Created a merged profile " + merged + " from " + first + " and " 812 + second); 813 loge("Merging data profiles will not be supported anymore. Please " 814 + "directly configure the merged profile " + merged + " in the APN " 815 + "config."); 816 dataProfiles.set(i, merged); 817 dataProfiles.remove(j); 818 } else { 819 j++; 820 } 821 } 822 i++; 823 } 824 } 825 826 /** 827 * Trigger anomaly report if APN Setting contains invalid info. 828 * 829 * @param setting The Apn setting to be checked. 830 */ checkApnSetting(@onNull ApnSetting setting)831 private void checkApnSetting(@NonNull ApnSetting setting) { 832 if (setting.canHandleType(ApnSetting.TYPE_MMS)) { 833 if (setting.getMmsc() == null) { 834 reportAnomaly("MMS is supported but no MMSC configured " + setting, 835 "9af73e18-b523-4dc5-adab-19d86c6a3685"); 836 } else if (!setting.getMmsc().toString().matches("^https?:\\/\\/.+")) { 837 reportAnomaly("Apn config mmsc should start with http but is " 838 + setting.getMmsc(), 839 "9af73e18-b523-4dc5-adab-ec754d959d4d"); 840 } 841 if (!TextUtils.isEmpty(setting.getMmsProxyAddressAsString()) 842 && setting.getMmsProxyAddressAsString().matches("^https?:\\/\\/.+")) { 843 reportAnomaly("Apn config mmsc_proxy should NOT start with http but is " 844 + setting.getMmsc(), "9af73e18-b523-4dc5-adab-ec754d959d4d"); 845 } 846 } 847 } 848 849 /** 850 * Trigger anomaly report if any two Apn Settings share the same APN name while having 851 * overlapped network types. 852 * 853 * @param profiles The list of data profiles to be checked. 854 */ checkDataProfiles(List<DataProfile> profiles)855 private void checkDataProfiles(List<DataProfile> profiles) { 856 for (int i = 0; i < profiles.size(); i++) { 857 ApnSetting a = profiles.get(i).getApnSetting(); 858 if (a == null) continue; 859 if (// Lingering network is not the default and doesn't cover all the regular networks 860 (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN 861 != a.getLingeringNetworkTypeBitmask() 862 && (a.getNetworkTypeBitmask() | a.getLingeringNetworkTypeBitmask()) 863 != a.getLingeringNetworkTypeBitmask()) { 864 reportAnomaly("Apn[" + a.getApnName() + "] network " 865 + TelephonyManager.convertNetworkTypeBitmaskToString( 866 a.getNetworkTypeBitmask()) + " should be a subset of " 867 + "the lingering network " 868 + TelephonyManager.convertNetworkTypeBitmaskToString( 869 a.getLingeringNetworkTypeBitmask()), 870 "9af73e18-b523-4dc5-adab-4bb24355d838"); 871 } 872 for (int j = i + 1; j < profiles.size(); j++) { 873 ApnSetting b = profiles.get(j).getApnSetting(); 874 if (b == null) continue; 875 String apnNameA = a.getApnName(); 876 String apnNameB = b.getApnName(); 877 if (TextUtils.equals(apnNameA, apnNameB) 878 // TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN means all network types 879 && (a.getNetworkTypeBitmask() 880 == (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN 881 || b.getNetworkTypeBitmask() 882 == (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN 883 || (a.getNetworkTypeBitmask() & b.getNetworkTypeBitmask()) != 0)) { 884 reportAnomaly("Found overlapped network type under the APN name " 885 + a.getApnName(), 886 "9af73e18-b523-4dc5-adab-4bb24555d839"); 887 } 888 } 889 } 890 } 891 892 /** 893 * Merge two data profiles if possible. 894 * 895 * @param dp1 Data profile 1 to be merged. 896 * @param dp2 Data profile 2 to be merged. 897 * 898 * @return The merged data profile. {@code null} if merging is not possible. 899 */ mergeDataProfiles( @onNull DataProfile dp1, @NonNull DataProfile dp2)900 private static @Nullable DataProfile mergeDataProfiles( 901 @NonNull DataProfile dp1, @NonNull DataProfile dp2) { 902 Objects.requireNonNull(dp1); 903 Objects.requireNonNull(dp2); 904 905 // We don't merge data profiles that have different traffic descriptor. 906 if (!Objects.equals(dp1.getTrafficDescriptor(), dp2.getTrafficDescriptor())) return null; 907 908 // If one of the APN setting is null, we don't merge. 909 if (dp1.getApnSetting() == null || dp2.getApnSetting() == null) return null; 910 911 // If two APN settings are not similar, we don't merge. 912 if (!dp1.getApnSetting().similar(dp2.getApnSetting())) return null; 913 914 // Start to merge APN setting 1 and 2. 915 ApnSetting apn1 = dp1.getApnSetting(); 916 ApnSetting apn2 = dp2.getApnSetting(); 917 ApnSetting.Builder apnBuilder = new ApnSetting.Builder(); 918 919 // Special handling id and entry name. We want to keep the default APN as it could be the 920 // preferred APN. 921 apnBuilder.setId(apn1.getId()); 922 apnBuilder.setEntryName(apn1.getEntryName()); 923 if (apn2.canHandleType(ApnSetting.TYPE_DEFAULT) 924 && !apn1.canHandleType(ApnSetting.TYPE_DEFAULT)) { 925 apnBuilder.setId(apn2.getId()); 926 apnBuilder.setEntryName(apn2.getEntryName()); 927 } 928 929 // Merge the following fields from apn1 and apn2. 930 apnBuilder.setProxyAddress(TextUtils.isEmpty(apn2.getProxyAddressAsString()) 931 ? apn1.getProxyAddressAsString() : apn2.getProxyAddressAsString()); 932 apnBuilder.setProxyPort(apn2.getProxyPort() == -1 933 ? apn1.getProxyPort() : apn2.getProxyPort()); 934 apnBuilder.setMmsc(apn2.getMmsc() == null ? apn1.getMmsc() : apn2.getMmsc()); 935 apnBuilder.setMmsProxyAddress(TextUtils.isEmpty(apn2.getMmsProxyAddressAsString()) 936 ? apn1.getMmsProxyAddressAsString() : apn2.getMmsProxyAddressAsString()); 937 apnBuilder.setMmsProxyPort(apn2.getMmsProxyPort() == -1 938 ? apn1.getMmsProxyPort() : apn2.getMmsProxyPort()); 939 apnBuilder.setUser(TextUtils.isEmpty(apn2.getUser()) ? apn1.getUser() : apn2.getUser()); 940 apnBuilder.setPassword(TextUtils.isEmpty(apn2.getPassword()) 941 ? apn1.getPassword() : apn2.getPassword()); 942 apnBuilder.setAuthType(apn2.getAuthType() == -1 943 ? apn1.getAuthType() : apn2.getAuthType()); 944 apnBuilder.setApnTypeBitmask(apn1.getApnTypeBitmask() | apn2.getApnTypeBitmask()); 945 apnBuilder.setMtuV4(apn2.getMtuV4() <= ApnSetting.UNSET_MTU 946 ? apn1.getMtuV4() : apn2.getMtuV4()); 947 apnBuilder.setMtuV6(apn2.getMtuV6() <= ApnSetting.UNSET_MTU 948 ? apn1.getMtuV6() : apn2.getMtuV6()); 949 950 // The following fields in apn1 and apn2 should be the same, otherwise ApnSetting.similar() 951 // should fail earlier. 952 apnBuilder.setApnName(apn1.getApnName()); 953 apnBuilder.setProtocol(apn1.getProtocol()); 954 apnBuilder.setRoamingProtocol(apn1.getRoamingProtocol()); 955 apnBuilder.setCarrierEnabled(apn1.isEnabled()); 956 apnBuilder.setNetworkTypeBitmask(apn1.getNetworkTypeBitmask()); 957 apnBuilder.setLingeringNetworkTypeBitmask(apn1.getLingeringNetworkTypeBitmask()); 958 apnBuilder.setProfileId(apn1.getProfileId()); 959 apnBuilder.setPersistent(apn1.isPersistent()); 960 apnBuilder.setMaxConns(apn1.getMaxConns()); 961 apnBuilder.setWaitTime(apn1.getWaitTime()); 962 apnBuilder.setMaxConnsTime(apn1.getMaxConnsTime()); 963 apnBuilder.setMvnoType(apn1.getMvnoType()); 964 apnBuilder.setMvnoMatchData(apn1.getMvnoMatchData()); 965 apnBuilder.setApnSetId(apn1.getApnSetId()); 966 apnBuilder.setCarrierId(apn1.getCarrierId()); 967 apnBuilder.setSkip464Xlat(apn1.getSkip464Xlat()); 968 apnBuilder.setAlwaysOn(apn1.isAlwaysOn()); 969 970 return new DataProfile.Builder() 971 .setApnSetting(apnBuilder.build()) 972 .setTrafficDescriptor(dp1.getTrafficDescriptor()) 973 .build(); 974 } 975 976 /** 977 * Check if the provided data profile is still compatible with the current environment. Note 978 * this method ignores APN id check and traffic descriptor check. A data profile with traffic 979 * descriptor only can always be used in any condition. 980 * 981 * @param dataProfile The data profile to check. 982 * @return {@code true} if the provided data profile can be still used in current environment. 983 */ isDataProfileCompatible(@onNull DataProfile dataProfile)984 public boolean isDataProfileCompatible(@NonNull DataProfile dataProfile) { 985 if (dataProfile == null) { 986 return false; 987 } 988 989 if (dataProfile.getApnSetting() == null && dataProfile.getTrafficDescriptor() != null) { 990 // A traffic descriptor only data profile can be always used. Traffic descriptors are 991 // always generated on the fly instead loaded from the database. 992 return true; 993 } 994 995 // Only check the APN from the profile is compatible or not. 996 return mAllDataProfiles.stream() 997 .filter(dp -> dp.getApnSetting() != null 998 && (dp.getApnSetting().getApnSetId() 999 == Telephony.Carriers.MATCH_ALL_APN_SET_ID 1000 || dp.getApnSetting().getApnSetId() == mPreferredDataProfileSetId)) 1001 .anyMatch(dp -> areDataProfilesSharingApn(dataProfile, dp)); 1002 1003 } 1004 1005 /** 1006 * @return {@code true} if both data profiles' APN setting are non-null and essentially the same 1007 * (non-essential elements include e.g.APN Id). 1008 */ areDataProfilesSharingApn(@ullable DataProfile a, @Nullable DataProfile b)1009 public boolean areDataProfilesSharingApn(@Nullable DataProfile a, @Nullable DataProfile b) { 1010 return a != null 1011 && b != null 1012 && a.getApnSetting() != null 1013 && a.getApnSetting().equals(b.getApnSetting(), 1014 mPhone.getServiceState().getDataRoamingFromRegistration()); 1015 } 1016 1017 /** 1018 * Register the callback for receiving information from {@link DataProfileManager}. 1019 * 1020 * @param callback The callback. 1021 */ registerCallback(@onNull DataProfileManagerCallback callback)1022 public void registerCallback(@NonNull DataProfileManagerCallback callback) { 1023 mDataProfileManagerCallbacks.add(callback); 1024 } 1025 1026 /** 1027 * Unregister the previously registered {@link DataProfileManagerCallback}. 1028 * 1029 * @param callback The callback to unregister. 1030 */ unregisterCallback(@onNull DataProfileManagerCallback callback)1031 public void unregisterCallback(@NonNull DataProfileManagerCallback callback) { 1032 mDataProfileManagerCallbacks.remove(callback); 1033 } 1034 1035 /** 1036 * Trigger the anomaly report with the specified UUID. 1037 * 1038 * @param anomalyMsg Description of the event 1039 * @param uuid UUID associated with that event 1040 */ reportAnomaly(@onNull String anomalyMsg, @NonNull String uuid)1041 private void reportAnomaly(@NonNull String anomalyMsg, @NonNull String uuid) { 1042 logl(anomalyMsg); 1043 AnomalyReporter.reportAnomaly(UUID.fromString(uuid), anomalyMsg, mPhone.getCarrierId()); 1044 } 1045 1046 /** 1047 * Log debug messages. 1048 * @param s debug messages 1049 */ log(@onNull String s)1050 private void log(@NonNull String s) { 1051 Rlog.d(mLogTag, s); 1052 } 1053 1054 /** 1055 * Log error messages. 1056 * @param s error messages 1057 */ loge(@onNull String s)1058 private void loge(@NonNull String s) { 1059 Rlog.e(mLogTag, s); 1060 } 1061 1062 /** 1063 * Log verbose messages. 1064 * @param s debug messages. 1065 */ logv(@onNull String s)1066 private void logv(@NonNull String s) { 1067 if (VDBG) Rlog.v(mLogTag, s); 1068 } 1069 1070 /** 1071 * Log debug messages and also log into the local log. 1072 * @param s debug messages 1073 */ logl(@onNull String s)1074 private void logl(@NonNull String s) { 1075 log(s); 1076 mLocalLog.log(s); 1077 } 1078 1079 /** 1080 * Dump the state of DataProfileManager 1081 * 1082 * @param fd File descriptor 1083 * @param printWriter Print writer 1084 * @param args Arguments 1085 */ dump(FileDescriptor fd, PrintWriter printWriter, String[] args)1086 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 1087 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 1088 pw.println(DataProfileManager.class.getSimpleName() + "-" + mPhone.getPhoneId() + ":"); 1089 pw.increaseIndent(); 1090 1091 pw.println("Data profiles for the current carrier:"); 1092 pw.increaseIndent(); 1093 for (DataProfile dp : mAllDataProfiles) { 1094 pw.print(dp); 1095 pw.println(", last setup time: " + DataUtils.elapsedTimeToString( 1096 dp.getLastSetupTimestamp())); 1097 } 1098 pw.decreaseIndent(); 1099 1100 pw.println("Preferred data profile=" + mPreferredDataProfile); 1101 pw.println("Preferred data profile from db=" + getPreferredDataProfileFromDb()); 1102 pw.println("Preferred data profile from config=" + getPreferredDataProfileFromConfig()); 1103 pw.println("Preferred data profile set id=" + mPreferredDataProfileSetId); 1104 pw.println("Initial attach data profile=" + mInitialAttachDataProfile); 1105 pw.println("isTetheringDataProfileExisting=" + isTetheringDataProfileExisting( 1106 TelephonyManager.NETWORK_TYPE_LTE)); 1107 pw.println("Permanent failed profiles="); 1108 pw.increaseIndent(); 1109 mAllDataProfiles.stream() 1110 .filter(dp -> dp.getApnSetting() != null && dp.getApnSetting().getPermanentFailed()) 1111 .forEach(pw::println); 1112 pw.decreaseIndent(); 1113 1114 pw.println("Local logs:"); 1115 pw.increaseIndent(); 1116 mLocalLog.dump(fd, pw, args); 1117 pw.decreaseIndent(); 1118 pw.decreaseIndent(); 1119 } 1120 } 1121