USB Preferences-Android13

USB Preferences

  • 1. USB preference setting interface and entrance
  • 2. USB function settings
    • 2.1 USB function corresponding mode
    • 2.2 Click Settings
    • 2.3 Broadcast monitoring refresh
  • 3. Log switch
    • 3.1 Evet log
    • 3.2 Log switch in code
    • 3.3 Key logs
  • 4. Abnormal

1. USB preference interface and entrance

Settings>Connected devices>USB
packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
packages/apps/Settings/res/xml/usb_details_fragment.xml

private static List<UsbDetailsController> createControllerList(Context context,
        UsbBackend usbBackend, UsbDetailsFragment fragment) {<!-- -->
    List<UsbDetailsController> ret = new ArrayList<>();
    ret.add(new UsbDetailsHeaderController(context, fragment, usbBackend));
    ret.add(new UsbDetailsDataRoleController(context, fragment, usbBackend));
    ret.add(new UsbDetailsFunctionsController(context, fragment, usbBackend));
    ret.add(new UsbDetailsPowerRoleController(context, fragment, usbBackend));
    ret.add(new UsbDetailsTranscodeMtpController(context, fragment, usbBackend));
    return ret;
}

2. USB function settings

packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java

2.1 USB function corresponding mode

FWK mode Corresponding value String display Node
FUNCTION_NONE = 0 NONE = 0
FUNCTION_MTP = GadgetFunction.MTP MTP = 1 << 2 “File Transfer ”
FUNCTION_PTP = GadgetFunction.PTP PTP = 1 << 4 “PTP”
FUNCTION_RNDIS = GadgetFunction.RNDIS RNDIS = 1 << 5 “USB tethering”
FUNCTION_MIDI = GadgetFunction.MIDI MIDI = 1 << 3 “MIDI”
FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY ACCESSORY = 1 << 1
FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE AUDIO_SOURCE = 1 << 6
FUNCTION_ADB = GadgetFunction.ADB ADB = 1 << 0 “Not used for data transmission”

frameworks/base/core/java/android/hardware/usb/UsbManager.java
hardware/interfaces/usb/gadget/1.0/types.hal
frameworks/base/core/proto/android/service/usb.proto

/* Same as android.hardware.usb.gadget.V1_0.GadgetFunction.* */
enum Function {
    FUNCTION_ADB = 1;
    FUNCTION_ACCESSORY = 2;
    FUNCTION_MTP = 4;
    FUNCTION_MIDI = 8;
    FUNCTION_PTP = 16;
    FUNCTION_RNDIS = 32;
    FUNCTION_AUDIO_SOURCE = 64;
}

2.2 Click Settings

mUsbBackend Set via UsbManager.java, UsbService.java, UsbDeviceManager.java

packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java

@Override
public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {<!-- -->
    final long function = UsbBackend.usbFunctionsFromString(preference.getKey());
    final long previousFunction = mUsbBackend.getCurrentFunctions();
    if (DEBUG) {<!-- -->
        Log.d(TAG, "onRadioButtonClicked() function : " + function + ", toString() : "
                 + UsbManager.usbFunctionsToString(function) + ", previousFunction : "
                 + previousFunction + ", toString() : "
                 + UsbManager.usbFunctionsToString(previousFunction));
    }
    if (function != previousFunction & amp; & amp; !Utils.isMonkeyRunning()
             & amp; & amp; !isClickEventIgnored(function, previousFunction)) {<!-- -->
        mPreviousFunction = previousFunction;

        //Update the UI in advance to make it looks smooth
        final SelectorWithWidgetPreference prevPref =
                (SelectorWithWidgetPreference) mProfilesContainer.findPreference(
                        UsbBackend.usbFunctionsToString(mPreviousFunction));
        if (prevPref != null) {<!-- -->
            prevPref.setChecked(false);
            preference.setChecked(true);
        }

        if (function == UsbManager.FUNCTION_RNDIS || function == UsbManager.FUNCTION_NCM) {<!-- -->
            // We need to have entitlement check for usb tethering, so use API in
            // TetheringManager.
            mTetheringManager.startTethering(
                    TetheringManager.TETHERING_USB, new HandlerExecutor(mHandler),
                    mOnStartTetheringCallback);
        } else {<!-- -->
            mUsbBackend.setCurrentFunctions(function);
        }
    }
}

frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java

public void setCurrentFunctions(long functions) {<!-- -->
        if (DEBUG) {<!-- -->
            Slog.d(TAG, "setCurrentFunctions(" + UsbManager.usbFunctionsToString(functions) + ")");
        }
        if (functions == UsbManager.FUNCTION_NONE) {<!-- -->
            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_CHARGING);
        } else if (functions == UsbManager.FUNCTION_MTP) {<!-- -->
            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MTP);
        } else if (functions == UsbManager.FUNCTION_PTP) {<!-- -->
            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_PTP);
        } else if (functions == UsbManager.FUNCTION_MIDI) {<!-- -->
            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MIDI);
        } else if (functions == UsbManager.FUNCTION_RNDIS) {<!-- -->
            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_RNDIS);
        } else if (functions == UsbManager.FUNCTION_ACCESSORY) {<!-- -->
            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_ACCESSORY);
        }
        mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions);
    }

        private boolean trySetEnabledFunctions(long usbFunctions, boolean forceRestart) {<!-- -->
            String functions = null;
            if (usbFunctions != UsbManager.FUNCTION_NONE) {<!-- -->
                functions = UsbManager.usbFunctionsToString(usbFunctions);
            }
            mCurrentFunctions = usbFunctions;
            if (functions == null || applyAdbFunction(functions)
                    .equals(UsbManager.USB_FUNCTION_NONE)) {<!-- -->
                functions = UsbManager.usbFunctionsToString(getChargingFunctions());
            }
            functions = applyAdbFunction(functions);

            String oemFunctions = applyOemOverrideFunction(functions);

            if (!isNormalBoot() & amp; & amp; !mCurrentFunctionsStr.equals(functions)) {<!-- -->
                setSystemProperty(getPersistProp(true), functions);
            }

            if ((!functions.equals(oemFunctions)
                     & amp; & amp; !mCurrentOemFunctions.equals(oemFunctions))
                    || !mCurrentFunctionsStr.equals(functions)
                    || !mCurrentFunctionsApplied
                    || forceRestart) {<!-- -->
                Slog.i(TAG, "Setting USB config to " + functions);
                mCurrentFunctionsStr = functions;
                mCurrentOemFunctions = oemFunctions;
                mCurrentFunctionsApplied = false;

                /**
                 * Kick the USB stack to close existing connections.
                 */
                setUsbConfig(UsbManager.USB_FUNCTION_NONE);

                if (!waitForState(UsbManager.USB_FUNCTION_NONE)) {<!-- -->
                    Slog.e(TAG, "Failed to kick USB config");
                    return false;
                }

                /**
                 * Set the new USB configuration.
                 */
                setUsbConfig(oemFunctions);

                if (mBootCompleted
                         & amp; & amp; (containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
                        || containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {<!-- -->
                    /**
                     * Start up dependent services.
                     */
                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                }

                if (!waitForState(oemFunctions)) {<!-- -->
                    Slog.e(TAG, "Failed to switch USB config to " + functions);
                    return false;
                }

                mCurrentFunctionsApplied = true;
            }
            return true;
        }

hardware/interfaces/usb/gadget/1.2/default/UsbGadget.cpp

V1_0::Status UsbGadget::setupFunctions(uint64_t functions,
                                       const sp<V1_0::IUsbGadgetCallback> & amp; callback,
                                       uint64_t timeout) {<!-- -->
    bool ffsEnabled = false;
    int i = 0;

    if (addGenericAndroidFunctions( & amp;monitorFfs, functions, & amp;ffsEnabled, & amp;i) !=
        V1_0::Status::SUCCESS)
        return V1_0::Status::ERROR;

    if ((functions & amp; V1_2::GadgetFunction::ADB) != 0) {<!-- -->
        ffsEnabled = true;
        if (addAdb( & amp;monitorFfs, & amp;i) != V1_0::Status::SUCCESS) return V1_0::Status::ERROR;
    }

    // Pull up the gadget right away when there are no ffs functions.
    if (!ffsEnabled) {<!-- -->
        if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) return V1_0::Status::ERROR;
        mCurrentUsbFunctionsApplied = true;
        if (callback) callback->setCurrentUsbFunctionsCb(functions, V1_0::Status::SUCCESS);
        return V1_0::Status::SUCCESS;
    }

    monitorFfs.registerFunctionsAppliedCallback( & amp;currentFunctionsAppliedCallback, this);
    // Monitors the ffs paths to pull up the gadget when descriptors are written.
    // Also takes of the pulling up the gadget again if the userspace process
    // dies and restarts.
    monitorFfs.startMonitor();

    if (kDebug) ALOGI("Mainthread in Cv");

    if (callback) {<!-- -->
        bool pullup = monitorFfs.waitForPullUp(timeout);
        Return<void> ret = callback->setCurrentUsbFunctionsCb(
                functions, pullup ? V1_0::Status::SUCCESS : V1_0::Status::ERROR);
        if (!ret.isOk()) ALOGE("setCurrentUsbFunctionsCb error %s", ret.description().c_str());
    }

    return V1_0::Status::SUCCESS;
}

Return<void> UsbGadget::setCurrentUsbFunctions(uint64_t functions,
                                               const sp<V1_0::IUsbGadgetCallback> & amp; callback,
                                               uint64_t timeout) {<!-- -->
    std::unique_lock<std::mutex> lk(mLockSetCurrentFunction);

    mCurrentUsbFunctions = functions;
    mCurrentUsbFunctionsApplied = false;

    // Unlink the gadget and stop the monitor if running.
    V1_0::Status status = tearDownGadget();
    if (status != V1_0::Status::SUCCESS) {<!-- -->
        goto error;
    }

    ALOGI("Returned from tearDown gadget");

    // Leave the gadget pulled down to give time for the host to sense disconnect.
    usleep(kDisconnectWaitUs);

    if (functions == static_cast<uint64_t>(V1_2::GadgetFunction::NONE)) {<!-- -->
        if (callback == NULL) return Void();
        Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, V1_0::Status::SUCCESS);
        if (!ret.isOk())
            ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str());
        return Void();
    }

    status = validateAndSetVidPid(functions);

    if (status != V1_0::Status::SUCCESS) {<!-- -->
        goto error;
    }

    status = setupFunctions(functions, callback, timeout);
    if (status != V1_0::Status::SUCCESS) {<!-- -->
        goto error;
    }

    ALOGI("Usb Gadget setcurrent functions called successfully");
    return Void();

error:
    ALOGI("Usb Gadget setcurrent functions failed");
    if (callback == NULL) return Void();
    Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, status);
    if (!ret.isOk())
        ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str());
    return Void();
}

hardware/interfaces/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp

Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled,
                                  int* functionCount) {<!-- -->
    if (((functions & amp; GadgetFunction::MTP) != 0)) {<!-- -->
        *ffsEnabled = true;
        ALOGI("setCurrentUsbFunctions mtp");
        if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;

        if (!monitorFfs->addInotifyFd("/dev/usb-ffs/mtp/")) return Status::ERROR;

        if (linkFunction("ffs.mtp", (*functionCount) + + )) return Status::ERROR;

        // Add endpoints to be monitored.
        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep1");
        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep2");
        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep3");
    } else if (((functions & amp; GadgetFunction::PTP) != 0)) {<!-- -->
        *ffsEnabled = true;
        ALOGI("setCurrentUsbFunctions ptp");
        if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;

        if (!monitorFfs->addInotifyFd("/dev/usb-ffs/ptp/")) return Status::ERROR;

        if (linkFunction("ffs.ptp", (*functionCount) + + )) return Status::ERROR;

        // Add endpoints to be monitored.
        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep1");
        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep2");
        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep3");
    }

    if ((functions & amp; GadgetFunction::MIDI) != 0) {<!-- -->
        ALOGI("setCurrentUsbFunctions MIDI");
        if (linkFunction("midi.gs5", (*functionCount) + + )) return Status::ERROR;
    }

    if ((functions & amp; GadgetFunction::ACCESSORY) != 0) {<!-- -->
        ALOGI("setCurrentUsbFunctions Accessory");
        if (linkFunction("accessory.gs2", (*functionCount) + + )) return Status::ERROR;
    }

    if ((functions & amp; GadgetFunction::AUDIO_SOURCE) != 0) {<!-- -->
        ALOGI("setCurrentUsbFunctions Audio Source");
        if (linkFunction("audio_source.gs3", (*functionCount) + + )) return Status::ERROR;
    }

    if ((functions & amp; GadgetFunction::RNDIS) != 0) {<!-- -->
        ALOGI("setCurrentUsbFunctions rndis");
        if (linkFunction("gsi.rndis", (*functionCount) + + )) return Status::ERROR;
        std::string rndisFunction = GetProperty(kVendorRndisConfig, "");
        if (rndisFunction != "") {<!-- -->
            if (linkFunction(rndisFunction.c_str(), (*functionCount) + + )) return Status::ERROR;
        } else {<!-- -->
            // link gsi.rndis for older pixel projects
            if (linkFunction("gsi.rndis", (*functionCount) + + )) return Status::ERROR;
        }
    }

    if ((functions & amp; GadgetFunction::NCM) != 0) {<!-- -->
        ALOGI("setCurrentUsbFunctions ncm");
        if (linkFunction("ncm.gs6", (*functionCount) + + )) return Status::ERROR;
    }

    return Status::SUCCESS;
}

Understand the USB Gadget HAL API architecture

2.3 Broadcast monitoring refresh

Broadcast listening refresh onUsbConnectionChanged > refresh
packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiver.java
packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java

/**
 * Interface definition for a callback to be invoked when usb connection is changed.
 */
interface UsbConnectionListener {<!-- -->
    void onUsbConnectionChanged(boolean connected, long functions, int powerRole, int dataRole,
            boolean isUsbConfigured);
}
private UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
            (connected, functions, powerRole, dataRole, isUsbFigured) -> {<!-- -->
                for (UsbDetailsController controller : mControllers) {<!-- -->
                    controller.refresh(connected, functions, powerRole, dataRole);
                }
            };

3. Log switch

3.1 Evet Log

For example: MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MTP);

frameworks/base/core/java/com/android/internal/logging/MetricsLogger.java
frameworks/base/core/java/android/metrics/LogMaker.java
frameworks/base/proto/src/metrics_constants/metrics_constants.proto

 // The view or control was activated.
   TYPE_ACTION = 4;
 
   // These values should never appear in log outputs - they are reserved for
   // internal platform metrics use.
   RESERVED_FOR_LOGBUILDER_CATEGORY = 757;
   RESERVED_FOR_LOGBUILDER_TYPE = 758;
   RESERVED_FOR_LOGBUILDER_SUBTYPE = 759;
 
   // ACTION: Usb config has been changed to charging
   // CATEGORY: SETTINGS
   // OS: P
   ACTION_USB_CONFIG_CHARGING = 1275;
 
   // ACTION: Usb config has been changed to mtp (file transfer)
   // CATEGORY: SETTINGS
   // OS: P
   ACTION_USB_CONFIG_MTP = 1276;
 
   // ACTION: Usb config has been changed to ptp (photo transfer)
   // CATEGORY: SETTINGS
   // OS: P
   ACTION_USB_CONFIG_PTP = 1277;
 
   // ACTION: Usb config has been changed to rndis (usb tethering)
   // CATEGORY: SETTINGS
   // OS: P
   ACTION_USB_CONFIG_RNDIS = 1278;
 
   // ACTION: Usb config has been changed to midi
   // CATEGORY: SETTINGS
   // OS: P
   ACTION_USB_CONFIG_MIDI = 1279;
 
   // ACTION: Usb config has been changed to accessory
   // CATEGORY: SETTINGS
   // OS: P
   ACTION_USB_CONFIG_ACCESSORY = 1280;

3.2 Log switch in code

Adb opening: Need to restart the Settings process

adb shell setprop log.tag.UsbFunctionsCtrl D
adb shell setprop log.tag.UsbBroadcastReceiver D

packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java

private static final String TAG = "UsbFunctionsCtrl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

3.3 Key log

start u|SettingsActivity: Switching to|UsbFunctionsCtrl:|UsbDetailsFragment:|UsbBroadcastReceiver:|UsbDeviceManager:|sysui_multi_action: [757,.*,758,4]

[email protected]:|libusbconfigfs:

4. Abnormal

Symlink failed: "Cannot create symlink %s -> %s errno:%d"

hardware/interfaces/usb/gadget/1.2/default/lib/include/UsbGadgetCommon.h

#define GADGET_PATH "/config/usb_gadget/g1/"
#define PULLUP_PATH GADGET_PATH "UDC"
#define PERSISTENT_BOOT_MODE "ro.bootmode"
#define VENDOR_ID_PATH GADGET_PATH "idVendor"
#define PRODUCT_ID_PATH GADGET_PATH "idProduct"
#define DEVICE_CLASS_PATH GADGET_PATH "bDeviceClass"
#define DEVICE_SUB_CLASS_PATH GADGET_PATH "bDeviceSubClass"
#define DEVICE_PROTOCOL_PATH GADGET_PATH "bDeviceProtocol"
#define DESC_USE_PATH GADGET_PATH "os_desc/use"
#define OS_DESC_PATH GADGET_PATH "os_desc/b.1"
#define CONFIG_PATH GADGET_PATH "configs/b.1/"
#define FUNCTIONS_PATH GADGET_PATH "functions/"
#define FUNCTION_NAME "function"
#define FUNCTION_PATH CONFIG_PATH FUNCTION_NAME
#define RNDIS_PATH FUNCTIONS_PATH "gsi.rndis"


hardware/interfaces/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp

int linkFunction(const char* function, int index) {
    char functionPath[kMaxFilePathLength];
    char link[kMaxFilePathLength];

    sprintf(functionPath, "%s%s", FUNCTIONS_PATH, function);
    sprintf(link, "%s%d", FUNCTION_PATH, index);
    if (symlink(functionPath, link)) {
        ALOGE("Cannot create symlink %s -> %s errno:%d", link, functionPath, errno);
        return -1;
    }
    return 0;
}

Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled,
                                  int* functionCount) {<!-- -->
    if (((functions & amp; GadgetFunction::MTP) != 0)) {<!-- -->
        *ffsEnabled = true;
        ALOGI("setCurrentUsbFunctions mtp");
        if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;

        if (!monitorFfs->addInotifyFd("/dev/usb-ffs/mtp/")) return Status::ERROR;

        if (linkFunction("ffs.mtp", (*functionCount) + + )) return Status::ERROR;

        // Add endpoints to be monitored.
        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep1");
        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep2");
        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep3");
    } else if (((functions & amp; GadgetFunction::PTP) != 0)) {<!-- -->
        *ffsEnabled = true;
        ALOGI("setCurrentUsbFunctions ptp");
        if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;

        if (!monitorFfs->addInotifyFd("/dev/usb-ffs/ptp/")) return Status::ERROR;

        if (linkFunction("ffs.ptp", (*functionCount) + + )) return Status::ERROR;

        // Add endpoints to be monitored.
        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep1");
        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep2");
        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep3");
    }

    if ((functions & amp; GadgetFunction::MIDI) != 0) {<!-- -->
        ALOGI("setCurrentUsbFunctions MIDI");
        if (linkFunction("midi.gs5", (*functionCount) + + )) return Status::ERROR;
    }

    if ((functions & amp; GadgetFunction::ACCESSORY) != 0) {<!-- -->
        ALOGI("setCurrentUsbFunctions Accessory");
        if (linkFunction("accessory.gs2", (*functionCount) + + )) return Status::ERROR;
    }

    if ((functions & amp; GadgetFunction::AUDIO_SOURCE) != 0) {<!-- -->
        ALOGI("setCurrentUsbFunctions Audio Source");
        if (linkFunction("audio_source.gs3", (*functionCount) + + )) return Status::ERROR;
    }

    if ((functions & amp; GadgetFunction::RNDIS) != 0) {<!-- -->
        ALOGI("setCurrentUsbFunctions rndis");
        if (linkFunction("gsi.rndis", (*functionCount) + + )) return Status::ERROR;
        std::string rndisFunction = GetProperty(kVendorRndisConfig, "");
        if (rndisFunction != "") {<!-- -->
            if (linkFunction(rndisFunction.c_str(), (*functionCount) + + )) return Status::ERROR;
        } else {<!-- -->
            // link gsi.rndis for older pixel projects
            if (linkFunction("gsi.rndis", (*functionCount) + + )) return Status::ERROR;
        }
    }

    if ((functions & amp; GadgetFunction::NCM) != 0) {<!-- -->
        ALOGI("setCurrentUsbFunctions ncm");
        if (linkFunction("ncm.gs6", (*functionCount) + + )) return Status::ERROR;
    }

    return Status::SUCCESS;
}