Add methods to read/update request headers

Test: atest ParcelTest
Bug: 123744028
Change-Id: I3fc738a81bf2e091145d13da89e9da111c3494c8
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index f779d6e..49592a8 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -599,17 +599,53 @@
     return mHasFds;
 }
 
+void Parcel::updateWorkSourceRequestHeaderPosition() const {
+    // Only update the request headers once. We only want to point
+    // to the first headers read/written.
+    if (!mRequestHeaderPresent) {
+        mWorkSourceRequestHeaderPosition = dataPosition();
+        mRequestHeaderPresent = true;
+    }
+}
+
 // Write RPC headers.  (previously just the interface token)
 status_t Parcel::writeInterfaceToken(const String16& interface)
 {
     const IPCThreadState* threadState = IPCThreadState::self();
     writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
+    updateWorkSourceRequestHeaderPosition();
     writeInt32(threadState->shouldPropagateWorkSource() ?
             threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
     // currently the interface identification token is just its name as a string
     return writeString16(interface);
 }
 
+bool Parcel::replaceCallingWorkSourceUid(uid_t uid)
+{
+    if (!mRequestHeaderPresent) {
+        return false;
+    }
+
+    const size_t initialPosition = dataPosition();
+    setDataPosition(mWorkSourceRequestHeaderPosition);
+    status_t err = writeInt32(uid);
+    setDataPosition(initialPosition);
+    return err == NO_ERROR;
+}
+
+uid_t Parcel::readCallingWorkSourceUid()
+{
+    if (!mRequestHeaderPresent) {
+        return IPCThreadState::kUnsetWorkSource;
+    }
+
+    const size_t initialPosition = dataPosition();
+    setDataPosition(mWorkSourceRequestHeaderPosition);
+    uid_t uid = readInt32();
+    setDataPosition(initialPosition);
+    return uid;
+}
+
 bool Parcel::checkInterface(IBinder* binder) const
 {
     return enforceInterface(binder->getInterfaceDescriptor());
@@ -634,6 +670,7 @@
       threadState->setStrictModePolicy(strictPolicy);
     }
     // WorkSource.
+    updateWorkSourceRequestHeaderPosition();
     int32_t workSource = readInt32();
     threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
     // Interface descriptor.
@@ -2889,6 +2926,8 @@
     mAllowFds = true;
     mOwner = nullptr;
     mOpenAshmemSize = 0;
+    mWorkSourceRequestHeaderPosition = 0;
+    mRequestHeaderPresent = false;
 
     // racing multiple init leads only to multiple identical write
     if (gMaxFds == 0) {
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 240b708..afdfe4f 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -395,6 +395,12 @@
     static size_t       getGlobalAllocSize();
     static size_t       getGlobalAllocCount();
 
+    bool                replaceCallingWorkSourceUid(uid_t uid);
+    // Returns the work source provided by the caller. This can only be trusted for trusted calling
+    // uid.
+    uid_t               readCallingWorkSourceUid();
+    void                readRequestHeaders() const;
+
 private:
     typedef void        (*release_func)(Parcel* parcel,
                                         const uint8_t* data, size_t dataSize,
@@ -429,6 +435,7 @@
     void                initState();
     void                scanForFds() const;
     status_t            validateReadData(size_t len) const;
+    void                updateWorkSourceRequestHeaderPosition() const;
                         
     template<class T>
     status_t            readAligned(T *pArg) const;
@@ -477,6 +484,9 @@
     mutable size_t      mNextObjectHint;
     mutable bool        mObjectsSorted;
 
+    mutable bool        mRequestHeaderPresent;
+    mutable size_t      mWorkSourceRequestHeaderPosition;
+
     mutable bool        mFdsKnown;
     mutable bool        mHasFds;
     bool                mAllowFds;