public abstract class BaseLoadingFragment extends Fragment {
    private View root;

    private View failedView;
    private TextView failedWarningView;
    private View loadingView;
    private Button btnReload;

    ViewStub loadingStub;
    ViewStub failedStub;
    ViewStub failedWarningStub;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        failedView = null;
        failedWarningView = null;
        loadingView = null;
        btnReload = null;
        return setupLoading(inflater, createContentView(inflater, container, savedInstanceState));
    }

    public abstract View createContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);

    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }

    protected abstract String getRequestTag();

    protected View setupLoading(LayoutInflater inflater, View view) {
        root = inflater.inflate(R.layout.fragment_base_loading, null, false);
        failedWarningStub = (ViewStub) root.findViewById(R.id.vs_failed_header);
        failedStub = (ViewStub) root.findViewById(R.id.vs_failed);
        loadingStub = (ViewStub) root.findViewById(R.id.vs_loading);

        RelativeLayout rlContent = (RelativeLayout) root.findViewById(R.id.rl_content);
        rlContent.addView(view);
        return root;
    }

    protected void showLoading() {
        if (loadingView == null) {
            loadingView = loadingStub.inflate();
        }
        dismissToLoad();
        loadingView.setVisibility(View.VISIBLE);
    }

    private void dismissToLoad() {
        if (loadingView != null) {
            loadingView.setVisibility(View.GONE);
        }
        if (failedView != null) {
            failedView.setVisibility(View.GONE);
        }
    }

    protected void dismissLoading() {
        if (loadingView != null) {
            loadingView.setVisibility(View.GONE);
        }
        if (failedView != null) {
            failedView.setVisibility(View.GONE);
        }
        if (failedWarningView != null) {
            failedWarningView.setVisibility(View.GONE);
        }
    }

    protected void dismissByFailed(int tag) {
        if (failedView == null) {
            failedView = failedStub.inflate();
            btnReload = (Button) failedView.findViewById(R.id.loading_btn_reload);
            btnReload.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    reload(v);
                }
            });
        }
        if (loadingView != null) {
            loadingView.setVisibility(View.GONE);
        }
        btnReload.setTag(tag);
        failedView.setVisibility(View.VISIBLE);
    }

    protected void failedWithContent(final int tag, String warning) {
        if (failedWarningView == null) {
            failedWarningView = (TextView) failedWarningStub.inflate();
            failedWarningView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    reload(v);
                }
            });
        }
        if (loadingView != null) {
            loadingView.setVisibility(View.GONE);
        }
        if (failedView != null) {
            failedView.setVisibility(View.GONE);
        }
        failedWarningView.setTag(tag);
        failedWarningView.setText(warning);
        failedWarningView.setVisibility(View.VISIBLE);
    }

    private void reload(View v) {
        int tag = (int) v.getTag();
        onReload(tag);
    }

    protected void onReload(int tag) {

    }
} public class UAFIntent {
    public static final String UAF_INTENT_TYPE_KEY = "UAFIntentType";
    public static final String DISCOVERY_DATA_KEY = "discoveryData";
    public static final String COMPONENT_NAME_KEY = "componentName";
    public static final String ERROR_CODE_KEY = "errorCode";
    public static final String MESSAGE_KEY = "message";
    public static final String ORIGIN_KEY = "origin";
    public static final String CHANNEL_BINDINGS_KEY = "channelBindings";
    public static final String RESPONSE_CODE_KEY = "responseCode";

    /**
     * This Android intent invokes the FIDO UAF Client to discover the available authenticators and capabilties.
     * The FIDO UAF Client generally will not show a UI associated with the handling of this intent,
     * but immediately return the JSON structure. The calling application cannot depend on this however,
     * as the FIDO UAF Client may show a UI for privacy purposes,
     * allowing the user to choose whether and which authenticators to disclose to the calling application.
     * <p/>
     * This intent must be invoked with startActivityForResult().
     *
     * @return
     */
    public static Intent getDiscoverIntent() {
        Intent intent = new Intent(Constants.ACTION_FIDO_OPERATION);
        intent.setType(Constants.FIDO_CLIENT_INTENT_MIME);

        Bundle bundle = new Bundle();
        bundle.putString(UAF_INTENT_TYPE_KEY, UAFIntentType.DISCOVER.name());
        intent.putExtras(bundle);

        return intent;
    }


    /**
     * An intent with this type is returned by the FIDO UAF Client as an argument to onActivityResult()
     * in response to receiving an intent of type DISCOVER.
     * If the resultCode passed to onActivityResult() is RESULT_OK,
     * and the intent extra errorCode is NO_ERROR, this intent has an extra, discoveryData,
     * containing a String representation of a DiscoveryData JSON dictionary with the available authenticators and capabilities.
     *
     * @param discoveryData
     * @param componentName
     * @param errorCode
     * @return
     */
    public static Intent getDiscoverResultIntent(String discoveryData, String componentName, short errorCode) {
        Intent intent = new Intent();

        Bundle bundle = new Bundle();
        bundle.putString(UAF_INTENT_TYPE_KEY, UAFIntentType.DISCOVER_RESULT.name());
        bundle.putString(DISCOVERY_DATA_KEY, discoveryData);
        bundle.putString(COMPONENT_NAME_KEY, componentName);
        bundle.putShort(ERROR_CODE_KEY, errorCode);
        intent.putExtras(bundle);

        return intent;
    }

    /**
     * This intent invokes the FIDO UAF Client to discover if it would be able to process the supplied message
     * without prompting the user. The action handling this intent should not show a UI to the user.
     * <p/>
     * This intent must be invoked with startActivityForResult().
     * <p/>
     * This intent requires the following extras:
     *
     * @param message containing a String representation of a UAFMessage representing the request message to test.
     * @param origin  an optional extra that allows a caller with the
     *                org.fidoalliance.uaf.permissions.ACT_AS_WEB_BROWSER permission to supply
     *                an RFC6454 Origin [RFC6454] string to be used instead of the application's own identity.
     * @return
     */
    public static Intent getCheckPolicyIntent(String message, String origin) {
        Intent intent = new Intent(Constants.ACTION_FIDO_OPERATION);
        intent.setType(Constants.FIDO_CLIENT_INTENT_MIME);

        Bundle bundle = new Bundle();
        bundle.putString(UAF_INTENT_TYPE_KEY, UAFIntentType.CHECK_POLICY.name());
        bundle.putString(MESSAGE_KEY, message);
        bundle.putString(ORIGIN_KEY, origin);
        intent.putExtras(bundle);

        return intent;
    }

    /**
     * This Android intent is returned by the FIDO UAF Client as an argument to onActivityResult()
     * in response to receiving a CHECK_POLICY intent.
     * In addition to the resultCode passed to onActivityResult(),
     * this intent has an extra,
     *
     * @param componentName
     * @param errorCode     containing an ErrorCode value indicating the specific error condition
     *                      or NO_ERROR if the FIDO UAF Client could process the message.
     * @return
     */
    public static Intent getCheckPolicyResultIntent(String componentName, short errorCode) {
        Intent intent = new Intent();

        Bundle bundle = new Bundle();
        bundle.putString(UAF_INTENT_TYPE_KEY, UAFIntentType.CHECK_POLICY_RESULT.name());
        bundle.putString(COMPONENT_NAME_KEY, componentName);
        bundle.putShort(ERROR_CODE_KEY, errorCode);
        intent.putExtras(bundle);

        return intent;
    }

    /**
     * This Android intent invokes the FIDO UAF Client to process the supplied request message
     * and return a response message ready for delivery to the FIDO UAF Server.
     * The sender should assume that the FIDO UAF Client will display a user interface
     * allowing the user to prepare this intent, for example, prompting the user to complete their verification ceremony.
     * <p/>
     * This intent must be invoked with startActivityForResult().
     * <p/>
     * This intent requires the following extras:
     *
     * @param uafMessage      containing a String representation of a UAFMessage representing the request message to process.
     * @param origin          an optional parameter that allows a caller with the
     *                        org.fidoalliance.uaf.permissions.ACT_AS_WEB_BROWSER permission to supply
     *                        an RFC6454 Origin [RFC6454] string to be used instead of the application's own identity.
     * @param channelBindings containing a String representation of a JSON dictionary as defined
     *                        by the ChannelBinding structure in the FIDO UAF Protocol Specification [UAFProtocol].
     * @return
     */
    public static Intent getUAFOperationIntent(String uafMessage, String origin, String channelBindings) {
        Intent intent = new Intent(Constants.ACTION_FIDO_OPERATION);
        intent.setType(Constants.FIDO_CLIENT_INTENT_MIME);

        Bundle bundle = new Bundle();
        bundle.putString(UAF_INTENT_TYPE_KEY, UAFIntentType.UAF_OPERATION.name());
        bundle.putString(MESSAGE_KEY, uafMessage);
        bundle.putString(ORIGIN_KEY, origin);
        bundle.putString(CHANNEL_BINDINGS_KEY, channelBindings);
        intent.putExtras(bundle);
        return intent;
    }

    /**
     * This intent is returned by the FIDO UAF Client as an argument to onActivityResult(),
     * in response to receiving a UAF_OPERATION intent.
     * If the resultCode passed to onActivityResult() is RESULT_CANCELLED,
     * If the resultCode passed to onActivityResult() is RESULT_OK, and the errorCode is NO_ERROR,
     *
     * @param componentName componentName
     * @param uafMessage    containing a String representation of a UAFMessage,
     *                      being the UAF protocol response message to be delivered to the FIDO Server.
     * @return intent
     */
    public static Intent getUAFOperationResultIntent(String componentName, String uafMessage) {
        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString(UAF_INTENT_TYPE_KEY, UAFIntentType.UAF_OPERATION_RESULT.name());
        bundle.putString(COMPONENT_NAME_KEY, componentName);
        bundle.putShort(ERROR_CODE_KEY, UAFClientError.NO_ERROR);
        bundle.putString(MESSAGE_KEY, uafMessage);
        intent.putExtras(bundle);

        return intent;
    }

    public static Intent getUAFOperationErrorIntent(String componentName, short errorCode) {
        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString(UAF_INTENT_TYPE_KEY, UAFIntentType.UAF_OPERATION_RESULT.name());
        bundle.putString(COMPONENT_NAME_KEY, componentName);
        bundle.putShort(ERROR_CODE_KEY, errorCode);
        intent.putExtras(bundle);

        return intent;
    }

    /**
     * This intent must be delivered to the FIDO UAF Client to indicate
     * the processing status of a FIDO UAF message delivered to the remote server.
     * This is especially important as a new registration may be considered by the client
     * to be in a pending state until it is communicated that the server accepted it.
     *
     * @param message
     * @param responseCode
     * @return
     */
    public static Intent getUAFOperationCompletionStatusIntent(String message, short responseCode) {
        Intent intent = new Intent(Constants.ACTION_FIDO_OPERATION);
        intent.setType(Constants.FIDO_CLIENT_INTENT_MIME);

        Bundle bundle = new Bundle();
        bundle.putString(UAF_INTENT_TYPE_KEY, UAFIntentType.UAF_OPERATION_COMPLETION_STATUS.name());
        bundle.putString(MESSAGE_KEY, message);
        bundle.putShort(RESPONSE_CODE_KEY, responseCode);
        intent.putExtras(bundle);

        return intent;
    }
} public class ASMIntent {
    public static final String MESSAGE_KEY = "message";

    public static Intent getASMIntent(){
        Intent intent = new Intent(Constants.ACTION_FIDO_OPERATION);
        intent.setType(Constants.FIDO_ASM_INTENT_MIME);

        return intent;
    }

    public static Intent getASMOperationIntent(String asmRequest) {
        Intent intent = new Intent(Constants.ACTION_FIDO_OPERATION);
        intent.setType(Constants.FIDO_ASM_INTENT_MIME);

        Bundle bundle = new Bundle();
        bundle.putString(MESSAGE_KEY, asmRequest);
        intent.putExtras(bundle);
        return intent;
    }

    public static Intent getASMOperationResultIntent(String asmResponse) {
        Intent intent = new Intent();

        Bundle bundle = new Bundle();
        bundle.putString(MESSAGE_KEY, asmResponse);
        intent.putExtras(bundle);
        return intent;
    }
} public class ASMException extends Exception {
    public short statusCode;

    public ASMException(short statusCode) {
        this.statusCode = statusCode;
    }
} public class AuthenticatorInfo {
    public int authenticatorIndex;
    public Version[] asmVersions;
    public boolean isUserEnrolled;
    public boolean hasSettings;
    public String aaid;
    public String assertionScheme;
    public int authenticationAlgorithm;
    public int[] attestationTypes;
    public long userVerification;
    public int keyProtection;
    public int matcherProtection;
    public long attachmentHint;
    public boolean isSecondFactorOnly;
    public boolean isRoamingAuthenticator;
    public String[] supportedExtensionIDs;
    public int tcDisplay;

    public String tcDisplayContentType;
    public DisplayPNGCharacteristicsDescriptor[] tcDisplayPNGCharacteristics;
    public String title;
    public String description;
    public String icon;

    public AuthenticatorInfo authenticatorIndex(int authenticatorIndex) {
        this.authenticatorIndex = authenticatorIndex;
        return this;
    }

    public AuthenticatorInfo asmVersions(Version[] asmVersions) {
        this.asmVersions = asmVersions;
        return this;
    }

    public AuthenticatorInfo isUserEnrolled(boolean isUserEnrolled) {
        this.isUserEnrolled = isUserEnrolled;
        return this;
    }

    public AuthenticatorInfo hasSettings(boolean hasSettings) {
        this.hasSettings = hasSettings;
        return this;
    }

    public AuthenticatorInfo aaid(String aaid) {
        this.aaid = aaid;
        return this;
    }

    public AuthenticatorInfo assertionScheme(String assertionScheme) {
        this.assertionScheme = assertionScheme;
        return this;
    }

    public AuthenticatorInfo authenticationAlgorithm(int authenticationAlgorithm) {
        this.authenticationAlgorithm = authenticationAlgorithm;
        return this;
    }

    public AuthenticatorInfo attestationTypes(int[] attestationTypes) {
        this.attestationTypes = attestationTypes;
        return this;
    }

    public AuthenticatorInfo userVerification(int userVerification) {
        this.userVerification = userVerification;
        return this;
    }

    public AuthenticatorInfo keyProtection(int keyProtection) {
        this.keyProtection = keyProtection;
        return this;
    }

    public AuthenticatorInfo matcherProtection(int matcherProtection) {
        this.matcherProtection = matcherProtection;
        return this;
    }

    public AuthenticatorInfo attachmentHint(long attachmentHint) {
        this.attachmentHint = attachmentHint;
        return this;
    }

    public AuthenticatorInfo isSecondFactorOnly(boolean isSecondFactorOnly) {
        this.isSecondFactorOnly = isSecondFactorOnly;
        return this;
    }

    public AuthenticatorInfo isRoamingAuthenticator(boolean isRoamingAuthenticator) {
        this.isRoamingAuthenticator = isRoamingAuthenticator;
        return this;
    }

    public AuthenticatorInfo supportedExtensionIDs(String[] supportedExtensionIDs) {
        this.supportedExtensionIDs = supportedExtensionIDs;
        return this;
    }

    public AuthenticatorInfo tcDisplay(int tcDisplay) {
        this.tcDisplay = tcDisplay;
        return this;
    }


} public class UAFMessage extends JsonSerializable {
    public String uafProtocolMessage;

    public UAFMessage(){
    }

    public UAFMessage(String uafProtocolMessage) {
        this.uafProtocolMessage = uafProtocolMessage;
    }

    @Override
    public String toJson() {
        return gson.toJson(this);
    }

    @Override
    public void loadFromJson(String json) {
        UAFMessage message = gson.fromJson(json, UAFMessage.class);
        this.uafProtocolMessage = message.uafProtocolMessage;
    }
} public abstract class ASMMessageHandler {

    public interface StateChangeListener {
        void onStateChange(Traffic.OpStat newState, Traffic.OpStat oldState);
    }

    public interface UAFErrorCallback {
        void onError(short errorCode);
    }

    public static final String TAG = ASMMessageHandler.class.getSimpleName();
    public static final int REQUEST_ASM_OPERATION = 1;

    public static final String REG_TAG = "\"Reg\"";
    public static final String AUTH_TAG = "\"Auth\"";
    public static final String DEREG_TAG = "\"Dereg\"";

    private static final String UPV_TAG = "\"upv\"";
    private static final String MAJOR_TAG = "major";
    private static final String MINOR_TAG = "minor";

    protected final Gson gson = new Gson();

    protected Traffic.OpStat mCurrentState = Traffic.OpStat.PREPARE;

    protected final AuthenticatorListFragment fragment;
    protected final String facetId;

    protected StateChangeListener stateChangeListener;

    private UAFErrorCallback uafErrorCallback;

    protected String asmPackage;

    public static ASMMessageHandler parseMessage(AuthenticatorListFragment fragment, String intentType, String uafMessage, String channelBinding, int callerUid) {
        if (UAFIntentType.UAF_OPERATION.name().equals(intentType)) {
            boolean versionLegal = false;
            try {
                String upvSub = uafMessage.substring(uafMessage.indexOf(UPV_TAG));
                String upv = upvSub.substring(upvSub.indexOf("{"), upvSub.indexOf("}") + 1);
                JSONObject jsonObject = new JSONObject(upv);
                if (jsonObject.getInt(MAJOR_TAG) == 1 && jsonObject.getInt(MINOR_TAG) == 0) {
                    versionLegal = true;
                }
            } catch (Exception ignored) {
            }
            if (versionLegal) {
                if (uafMessage.contains(REG_TAG)) {
                    return new Reg(fragment, uafMessage, channelBinding, callerUid);
                } else if (uafMessage.contains(AUTH_TAG)) {
                    return new Auth(fragment, uafMessage, channelBinding, callerUid);
                } else if (uafMessage.contains(DEREG_TAG)) {
                    return new Dereg(fragment, uafMessage, callerUid);
                }
            }
        } else if (UAFIntentType.CHECK_POLICY.name().equals(intentType)) {
            return new CheckPolicy(fragment, uafMessage, callerUid);
        } else if (UAFIntentType.DISCOVER.name().equals(intentType)) {
            return new Discover(fragment, callerUid);
        } else if (UAFIntentType.UAF_OPERATION_COMPLETION_STATUS.name().equals(intentType)) {
            return new Completion(fragment, callerUid);
        }
        return new ASMMessageHandler(fragment, callerUid) {
            @Override
            public void start() {
                error(UAFClientError.PROTOCOL_ERROR);
            }

            @Override
            public void trafficStart() {
                error(UAFClientError.PROTOCOL_ERROR);
            }

            @Override
            public void traffic(String asmResponseMsg) {
                error(UAFClientError.PROTOCOL_ERROR);
            }
        };
    }

    public void setStateChangeListener(StateChangeListener listener) {
        stateChangeListener = listener;
    }

    public void setUafErrorCallback(UAFErrorCallback callback) {
        uafErrorCallback = callback;
    }

    public ASMMessageHandler(AuthenticatorListFragment fragment, int callerUid) {
        this.fragment = fragment;
        this.facetId = Utils.getFacetId(fragment.getActivity().getApplication(), callerUid);
    }

    public abstract void start();

    public abstract void trafficStart();

    public abstract void traffic(String asmResponseMsg) throws ASMException;

    public String getCurrentOpDescription() {
        return null;
    }

    public void setAsmPackage(String asmPackage) {
        this.asmPackage = asmPackage;
    }

    public Policy getPolicy() {
        return null;
    }

    protected boolean checkHeader(OperationHeader header) {
        if (header == null) {
            return false;
        }
        if (header.appID == null || header.appID.length() > Constants.APP_ID_MAX_LEN) {
            return false;
        }
        if (header.appID.length() > 0 && !header.appID.contains(Constants.APP_ID_PREFIX) && !header.appID.equals(facetId)) {
            return false;
        }
        return true;
    }

    protected boolean checkChallenge(String challenge) {
        if (TextUtils.isEmpty(challenge)) {
            return false;
        }
        if (challenge.length() < Constants.CHALLENGE_MIN_LEN || challenge.length() > Constants.CHALLENGE_MAX_LEN) {
            return false;
        }
        if (!challenge.matches(Constants.BASE64_REGULAR)) {
            return false;
        }
        return true;
    }

    protected boolean checkPolicy(Policy policy) {
        if (policy == null || policy.accepted == null) {
            return false;
        }
        return true;
    }

    protected void getTrustFacetId(String appId, final String facetId) {
        if (appId.contains(Constants.APP_ID_PREFIX)) {
            fragment.loading();
            RequestQueue requestQueue = RequestQueueHelper.getInstance(fragment.getActivity().getApplicationContext());
            GetRequest<FacetIdListResponse> request = new GetRequest<>(appId, FacetIdListResponse.class,
                    new Response.Listener() {
                        @Override
                        public void onResponse(Object response) {
                            fragment.loadingComplete();
                            if (checkFacetId(facetId, ((FacetIdListResponse) response).trustedFacets)) {
                                trafficStart();
                            } else {
                                error(UAFClientError.UNTRUSTED_FACET_ID);
                            }
                        }
                    },
                    new Response.ErrorListener() {
                        @Override
                        public void onErrorResponse(VolleyError error) {
                            fragment.loadingComplete();
                            getTrustFacetIdError();
                        }
                    }
            );
            request.setTag(TAG);
            requestQueue.add(request);
        } else {
            if (TextUtils.isEmpty(appId) || appId.equals(facetId)) {
                trafficStart();
            }
        }
    }

    protected void getTrustFacetIdError() {
        error(UAFClientError.UNTRUSTED_FACET_ID);
    }

    protected void updateState(Traffic.OpStat newState) {
        StatLog.printLog(TAG, "update op state:" + mCurrentState.name() + "->" + newState.name());
        if (stateChangeListener != null) {
            stateChangeListener.onStateChange(newState, mCurrentState);
        }
        mCurrentState = newState;
    }

    protected short handleGetInfo(String msg, AuthenticatorAdapter.OnAuthenticatorClickCallback callback) {
        StatLog.printLog(TAG, "get info :" + msg);
        ASMResponse asmResponse = ASMResponse.fromJson(msg, GetInfoOut.class);
        if (asmResponse.statusCode != StatusCode.UAF_ASM_STATUS_OK) {
            return UAFClientError.PROTOCOL_ERROR;
        }
        GetInfoOut getInfoOut = (GetInfoOut) asmResponse.responseData;
        AuthenticatorInfo[] authenticatorInfos = getInfoOut.Authenticators;
        StatLog.printLog(TAG, "client reg parse policy: " + gson.toJson(authenticatorInfos));
        if (authenticatorInfos == null) {
            return UAFClientError.NO_SUITABLE_AUTHENTICATOR;
        }
        List<AuthenticatorInfo> parseResult = parsePolicy(getPolicy(), authenticatorInfos);
        if (parseResult == null || parseResult.size() == 0) {
            return UAFClientError.NO_SUITABLE_AUTHENTICATOR;
        }
        fragment.showAuthenticator(parseResult, callback);
        return UAFClientError.NO_ERROR;
    }


    protected void error(short errorCode) {
        if (uafErrorCallback != null) {
            uafErrorCallback.onError(errorCode);
        }
    }

    public static String getInfoRequest(Version version) {
        StatLog.printLog(TAG, "asm request get info");
        ASMRequest<RegisterIn> asmRequest = new ASMRequest<>();
        asmRequest.requestType = Request.GetInfo;
        asmRequest.asmVersion = version;
        Gson gson = new GsonBuilder().setExclusionStrategies(getExclusionStrategy())
                .create();
        String asmRequestMsg = gson.toJson(asmRequest);
        StatLog.printLog(TAG, "asm request: " + asmRequestMsg);
        return asmRequestMsg;
    }

    private static ExclusionStrategy getExclusionStrategy() {
        return new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return f.getName().equals(ASMRequest.authenticatorIndexName);
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return false;
            }
        };
    }

    public static boolean checkFacetId(String facetId, ArrayList<FacetIdList> facetIdList) {
        if (facetIdList != null && facetIdList.size() > 0) {
            FacetIdList facetIdList1 = facetIdList.get(0);
            if (facetIdList1.ids != null) {
                for (String id : facetIdList1.ids) {
                    if (id.equals(facetId)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    protected static List<AuthenticatorInfo> parsePolicy(Policy policy, AuthenticatorInfo[] authenticatorInfos) {
        if (policy == null) {
            return Arrays.asList(authenticatorInfos);
        }
        List<AuthenticatorInfo> authenticatorInfoList = new ArrayList<>();
        for (AuthenticatorInfo info : authenticatorInfos) {
            for (MatchCriteria[] criterias : policy.accepted) {
                boolean setMatch = true;
                for (MatchCriteria criteria : criterias) {
                    if (!criteria.isMatch(info)) {
                        setMatch = false;
                        break;
                    }
                }
                if (setMatch) {
                    authenticatorInfoList.add(info);
                    break;
                }
            }
        }

        if (policy.disallowed != null) {
            for (AuthenticatorInfo info : authenticatorInfoList) {
                for (MatchCriteria matchCriteria : policy.disallowed) {
                    if (matchCriteria.isMatch(info)) {
                        authenticatorInfoList.remove(info);
                        break;
                    }
                }
            }
        }
        return authenticatorInfoList;
    }

} public class Traffic {
    public enum OpStat {
        PREPARE,
        GET_INFO_PENDING,
        REG_PENDING,
        AUTH_PENDING,
        DEREG_PENDING,

        PICK_AUTHENTICATOR_PENDING,
    }
} public class AuthenticatorAdapter extends RecyclerView.Adapter<AuthenticatorItemViewHolder> {
    public interface OnAuthenticatorClickCallback {
        void onAuthenticatorClick(AuthenticatorInfo info);
    }

    private final Context mContext;
    private final List<AuthenticatorInfo> mInfos;
    private final OnAuthenticatorClickCallback mCallback;

    public AuthenticatorAdapter(Context context, List<AuthenticatorInfo> infos, OnAuthenticatorClickCallback callback) {
        mContext = context;
        mInfos = infos;
        mCallback = callback;
    }

    @Override
    public AuthenticatorItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = View.inflate(mContext, R.layout.authenticator_item, null);
        return new AuthenticatorItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(AuthenticatorItemViewHolder holder, int position) {
        final AuthenticatorInfo info = mInfos.get(position);
        if (TextUtils.isEmpty(info.title)) {
            info.title = mContext.getString(R.string.unknown_device);
        }
        holder.tvTitle.setText(info.title);
//        if (TextUtils.isEmpty(info.icon)) {
        holder.ivIcon.setImageResource(R.drawable.ic_fp_40px);
//        }
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mCallback != null) {
                    mCallback.onAuthenticatorClick(info);
                }
            }
        });
    }

    @Override
    public int getItemCount() {
        return mInfos == null ? 0 : mInfos.size();
    }
} public class UAFClientActivity extends AppCompatActivity implements AsmListFragment.AsmItemPickListener {
    private static final String TAG = UAFClientActivity.class.getSimpleName();

    private static final String ASM_INFO_SP = "asm_pack";
    private static final String ASM_PACK_KEY = "asm_pack_key";
    private static final String ASM_APP_NAME_KEY = "asm_app_name_key";

    private String intentType;
    private String message;
    private String channelBinding;

    private int callerUid;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fido_client);
        Bundle extras = this.getIntent().getExtras();
        intentType = extras.getString(UAFIntent.UAF_INTENT_TYPE_KEY);
        message = extras.getString(UAFIntent.MESSAGE_KEY);
        channelBinding = extras.getString(UAFIntent.CHANNEL_BINDINGS_KEY);
        callerUid = getUid();
        StatLog.printLog(TAG, "onCreate intentType:" + intentType + " message:" + message + " channelBinding:" + channelBinding);

        if (TextUtils.isEmpty(getAsmPack(getApplicationContext()))) {
            choseAsm();
        } else {
            showAuthenticator();
        }
    }

    private void choseAsm() {
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.fl_content, AsmListFragment.getInstance(this))
                .commit();
    }

    private void showAuthenticator() {
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.fl_content, AuthenticatorListFragment.getInstance(intentType, message, channelBinding, callerUid))
                .commit();
    }

    private int getUid() {
        final PackageManager pm = getPackageManager();
        try {
            return pm.getActivityInfo(getCallingActivity(), PackageManager.GET_INTENT_FILTERS).applicationInfo.uid;
        } catch (Exception e) {
            return -1;
        }
    }

    @Override
    public void onBackPressed() {
        finishWithError(UAFClientError.USER_CANCELLED);
    }

    private void setFailedIntent(short errorCode) {
        Intent intent = UAFIntent.getUAFOperationErrorIntent(getComponentName().flattenToString(), errorCode);
        setResult(Activity.RESULT_CANCELED, intent);
    }

    private void finishWithError(short errorCode) {
        setFailedIntent(errorCode);
        finish();
    }

    @Override
    public void onAsmItemPick(AsmInfo info) {
        setAsmInfo(getApplicationContext(), info);
        showAuthenticator();
    }

    public static String getAsmPack(Context context) {
        SharedPreferences sp = context.getSharedPreferences(
                ASM_INFO_SP, Context.MODE_PRIVATE);

        return sp.getString(ASM_PACK_KEY, null);
    }

    public static AsmInfo getAsmInfo(Context context) {
        SharedPreferences sp = context.getSharedPreferences(
                ASM_INFO_SP, Context.MODE_PRIVATE);
        String name = sp.getString(ASM_APP_NAME_KEY, null);
        String pack = sp.getString(ASM_PACK_KEY, null);
        ResolveInfo resolveInfo = resolve(context, pack);
        AsmInfo info = new AsmInfo();
        info.appName(name)
                .pack(pack);
        if (resolveInfo != null) {
            info.icon = resolveInfo.loadIcon(context.getPackageManager());
        }

        return info;
    }

    public static void setAsmInfo(Context context, AsmInfo info) {
        if (info == null) {
            info = new AsmInfo();
        }
        SharedPreferences sp = context.getSharedPreferences(
                ASM_INFO_SP, Context.MODE_PRIVATE);
        sp.edit().putString(ASM_PACK_KEY, info.pack)
                .putString(ASM_APP_NAME_KEY, info.appName)
                .apply();
    }

    private static ResolveInfo resolve(Context context, String pack) {
        if (TextUtils.isEmpty(pack)) {
            return null;
        }
        Intent intent = ASMIntent.getASMIntent();
        intent.setPackage(pack);

        List<ResolveInfo> infos = context.getPackageManager().queryIntentActivities(intent, PackageManager.GET_INTENT_FILTERS);
        if (infos != null && infos.size() > 0) {
            return infos.get(0);
        } else {
            return null;
        }
    }
} public class StatLog {

    private static final String FILE_NAME = "FidoUAF/stat_log.txt";

    public static synchronized boolean isLogEnabled() {
        if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            return false;
        }
//        if (BuildConfig.DEBUG) {
//            return true;
//        }
        return false;
//        return true;
    }

    public static void printLog(String tag, String msg) {
        if (!isLogEnabled()) {
            return;
        }
        String procInfo = "Process id: " + Process.myPid()
                + " Thread id: " + Thread.currentThread().getId() + " ";

        Log.d(tag, procInfo + msg);

        writeLog(tag, procInfo + msg);
    }

    private static synchronized void writeLog(String tag, String msg) {
        internalWriteLog(FILE_NAME, tag, msg);
    }

    private static synchronized void internalWriteLog(String filename, String tag, String msg) {
        try {
            if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                return;
            }

            File file = new File(Environment.getExternalStorageDirectory(), filename);
            file.getParentFile().mkdirs();

            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));

            String time = getCurrentTime();
            bw.write(time + " " + tag + " \t" + msg + "\r\n");

            bw.close();
        } catch (Exception e) {
            // ignore
        }
    }

    private static String getCurrentTime() {
        Calendar c = Calendar.getInstance();
        return String.format(Locale.getDefault(), "%d-%02d-%02d %02d:%02d:%02d.%03d",
                c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH),
                c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), c.get(Calendar.SECOND),
                c.get(Calendar.MILLISECOND));
    }
}
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.jin.uitoolkit.fragment.BaseLoadingFragment;
import com.yalin.fidoclient.R;
import com.yalin.fidoclient.api.UAFIntent;
import com.yalin.fidoclient.asm.api.ASMIntent;
import com.yalin.fidoclient.asm.exceptions.ASMException;
import com.yalin.fidoclient.asm.msg.obj.AuthenticatorInfo;
import com.yalin.fidoclient.msg.client.UAFMessage;
import com.yalin.fidoclient.op.ASMMessageHandler;
import com.yalin.fidoclient.op.traffic.Traffic;
import com.yalin.fidoclient.ui.AuthenticatorAdapter;
import com.yalin.fidoclient.ui.UAFClientActivity;
import com.yalin.fidoclient.utils.StatLog;
import java.util.List;
/*
 * Copyright 2016 YaLin Jin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.yalin.fidoclient.ui.fragment;




/**
 * Created by YaLin on 2016/2/26.
 */
public class AuthenticatorListFragment extends BaseLoadingFragment implements ASMMessageHandler.StateChangeListener, ASMMessageHandler.UAFErrorCallback {
    private static final String TAG = AuthenticatorListFragment.class.getSimpleName();

    private View mCoordinator;
    private TextView tvOpType;
    private TextView tvInfo;
    private RecyclerView rvAuthenticators;

    private String mIntentType;
    private String mMessage;
    private String mChannelBinding;

    private int mCallerUid;

    private ASMMessageHandler asmMessageHandler;

    public static AuthenticatorListFragment getInstance(String intentType, String message, String channelBinding, int uid) {
        AuthenticatorListFragment fragment = new AuthenticatorListFragment();
        fragment.mIntentType = intentType;
        fragment.mMessage = message;
        fragment.mChannelBinding = channelBinding;
        fragment.mCallerUid = uid;
        return fragment;

    }

    @Override
    public View createContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_authenticator_list, container, false);
        findView(view);
        initData();
        return view;
    }

    @Override
    protected String getRequestTag() {
        return TAG;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        doConfirm();
    }

    private void findView(View view) {
        mCoordinator = view.findViewById(R.id.root_coordinator);
        rvAuthenticators = (RecyclerView) view.findViewById(R.id.rv_authenticators);
        tvOpType = (TextView) view.findViewById(R.id.tv_op_type);
        tvInfo = (TextView) view.findViewById(R.id.tv_prompt);

        rvAuthenticators.setLayoutManager(new GridLayoutManager(getActivity(), 2));
    }

    private void initData() {

    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == ASMMessageHandler.REQUEST_ASM_OPERATION) {
                String resultStr = data.getExtras().getString(ASMIntent.MESSAGE_KEY);
                try {
                    asmMessageHandler.traffic(resultStr);
                } catch (ASMException e) {
                    Snackbar.make(mCoordinator, ASMException.class.getSimpleName() + ":" + e.statusCode, Snackbar.LENGTH_SHORT)
                            .show();
                }
            }
        }
    }

    public void showAuthenticator(List<AuthenticatorInfo> infos, AuthenticatorAdapter.OnAuthenticatorClickCallback callback) {
        if (getActivity() != null) {
            AuthenticatorAdapter adapter = new AuthenticatorAdapter(getActivity(), infos, callback);
            rvAuthenticators.setAdapter(adapter);
        }
    }

    private void doConfirm() {
        if (TextUtils.isEmpty(mIntentType)) {
            showError(R.string.client_op_type_error);
            return;
        }
        processMessage(mMessage);
    }

    private void processMessage(String inUafOperationMsg) {
        StatLog.printLog(TAG, "process message:" + inUafOperationMsg);